基于Token认证的登录功能实现

上篇文章我们讲到了过滤器和拦截器理论知识以及 SpringBoot 集成过滤器和拦截器,本篇文章我们使用过滤器和拦截器去实现基于 Token 认证的登录功能。

一、登录校验 Filter 实现

1.1、Filter 校验流程图

  • 获得请求 url
  • 判断请求 url 中是否包含 login ,如果包含,说明是登录操作,放行。
  • 获取请求头中的令牌(Token
  • 判断令牌是否存在,如果不存在,返回错误结果(未登录)。
  • 解析 token ,如果解析失败,返回错误结果(未登录)。
  • 放行

1.2、Filter 校验实现

新建一个 SpringBoot 项目 loginFilter,引入之前文章提到的 JWT 工具类。

JWTUtils代码如下:

java 复制代码
package com.duan.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.Map;

/**
 * @author db
 * @version 1.0
 * @description JWTUtils
 * @since 2023/12/31
 */
public class JWTUtils {

    // 密钥
    private static String signKey = "cxykk1217";
    // 过期时间
    private static Long expire = 1000L*60*30; // 30分钟

    /**
     * 生成JWT
     * @param claims JWT第二部分负载payload中存储的内容
     * @return
     */
    public static String generateJwt(Map<String,Object> claims){
        String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256, signKey)
                .addClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                .compact();

        return jwt;
    }

    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser().setSigningKey(signKey)
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }
}

新建 LoginFilter 类并实现 Filter ,在 doFilter 方法中进行登录校验。代码如下:

java 复制代码
package com.duan.filter;

import com.alibaba.fastjson.JSONObject;
import com.duan.pojo.Result;
import com.duan.utils.JWTUtils;
import org.springframework.util.StringUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author db
 * @version 1.0
 * @description LoginFilter
 * @since 2024/1/11
 */
@WebFilter(urlPatterns = "/*")
public class LoginFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        // 1. 获得请求路径
        String url = httpServletRequest.getRequestURL().toString();

        // 2. 判断请求的资源路径
        if(url.contains("/login")){
            // 登录功能,放行
            chain.doFilter(request,response);
            return;
        }

        // 3. 获取请求头token
        String token = httpServletRequest.getHeader("token");
        if(token == null){
            // 返回登录页面
            Result noLogin = Result.error("NO_LOGIN");
            // 使用原始方式给客户端响应数据
            // 把noLogin 对象转成json字符串返回
            String jsonString = JSONObject.toJSONString(noLogin);
            httpServletResponse.getWriter().write(jsonString);
            return;
        }
        // 4. 解析token
        try{
            JWTUtils.parseJWT(token);
        }catch (Exception e){
            // token存在问题
            // 返回登录页面
            Result noLogin = Result.error("NO_LOGIN");
            // 使用原始方式给客户端响应数据
            // 把noLogin 对象转成json字符串返回
            String jsonString = JSONObject.toJSONString(noLogin);
            httpServletResponse.getWriter().write(jsonString);
            return;
        }
        // 5. token解析成功,放行
        chain.doFilter(request,response);
    }
}

注意:使用 @WebFilter 配置过滤器时,启动类上一定要使用 @ServletComponentScan 注解。

启动程序,通过 postman 来访问 login 和 getUser 方法。

代码地址:https://gitee.com/duan138/practice-code/tree/dev/loginFilter

二、登录校验拦截器实现

2.1、Interceptor 校验流程图

  • 获得请求 url
  • 判断请求 url 中是否包含 login ,如果包含,说明是登录操作,放行。
  • 获取请求头中的令牌(Token
  • 判断令牌是否存在,如果不存在,返回错误结果(未登录)。
  • 解析 token ,如果解析失败,返回错误结果(未登录)。
  • 放行

通过流程图可以看出,过滤器和拦截器实现的流程是一样的,只不过实现方式不一样。

2.2、Interceptor 校验

新建一个 SpringBoot 项目 loginInterceptor,引入之前文章提到的 JWT 工具类。

JWTUtils 代码如下:

java 复制代码
package com.duan.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.Map;

/**
 * @author db
 * @version 1.0
 * @description JWTUtils
 * @since 2023/12/31
 */
public class JWTUtils {

    // 密钥
    private static String signKey = "cxykk1217";
    // 过期时间
    private static Long expire = 1000L*60*30; // 30分钟

    /**
     * 生成JWT
     * @param claims JWT第二部分负载payload中存储的内容
     * @return
     */
    public static String generateJwt(Map<String,Object> claims){
        String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256, signKey)
                .addClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                .compact();

        return jwt;
    }

    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser().setSigningKey(signKey)
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }
}

新建 LoginInterceptor 类并实现 HandlerInterceptor,在 preHandle 方法中进行登录校验。代码如下:

java 复制代码
package com.duan.handler;

import com.alibaba.fastjson.JSONObject;
import com.duan.pojo.Result;
import com.duan.utils.JWTUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author db
 * @version 1.0
 * @description LoginInterceptor
 * @since 2023/12/20
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {

    // 目标方法执行前调用  true:放行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        // 1. 获得请求路径
        String url = request.getRequestURL().toString();

        // 2. 判断请求的资源路径
        if(url.contains("/login")){
            // 登录功能,放行
            return true;
        }

        // 3. 获取请求头token
        String token = request.getHeader("token");
        if(token == null){
            // 返回登录页面
            Result noLogin = Result.error("NO_LOGIN");
            // 使用原始方式给客户端响应数据
            // 把noLogin 对象转成json字符串返回
            String jsonString = JSONObject.toJSONString(noLogin);
            response.getWriter().write(jsonString);
            return false;
        }
        // 4. 解析token
        try{
            JWTUtils.parseJWT(token);
        }catch (Exception e){
            // token存在问题
            // 返回登录页面
            Result noLogin = Result.error("NO_LOGIN");
            // 使用原始方式给客户端响应数据
            // 把noLogin 对象转成json字符串返回
            String jsonString = JSONObject.toJSONString(noLogin);
            response.getWriter().write(jsonString);
            return false;
        }
        // 5. token解析成功,放行
        return true;
    }

    // 目标方法执行后调用
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle...");
    }

    // 请求处理后调用
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("Completion...");
    }
}

在 config 包中 LoginInterceptorConfig 方法配置新建的 loginInterceptor。

java 复制代码
package com.duan.config;

import com.duan.handler.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author db
 * @version 1.0
 * @description LoginInterceptorConfig  注册拦截器
 * @since 2023/12/20
 */
@Configuration
public class LoginInterceptorConfig implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(loginInterceptor).addPathPatterns("/**");

    }
}

启动程序,通过 postman 来访问 login 和 getUser 方法。

代码地址:https://gitee.com/duan138/practice-code/tree/dev/loginInterceptor

三、总结

通过过滤器和拦截器实现基于 Token 的登录功能,加深了对过滤器和拦截器的理解,同时也梳理了基于 Token 认证登录的流程,在现在项目中常用的是基于 SpringSecurity + JWT 登录认证方式,后续我们来看一看基于 Spring security + JWT 怎么去实现登录控制。


参考

1.https://space.bilibili.com/1809189461


改变你能改变的,接受你不能改变的,关注公众号:程序员康康,一起成长,共同进步。

相关推荐
LuckyLay4 分钟前
Spring学习笔记_27——@EnableLoadTimeWeaving
java·spring boot·spring
向阳121816 分钟前
Dubbo负载均衡
java·运维·负载均衡·dubbo
Gu Gu Study26 分钟前
【用Java学习数据结构系列】泛型上界与通配符上界
java·开发语言
WaaTong1 小时前
《重学Java设计模式》之 原型模式
java·设计模式·原型模式
m0_743048441 小时前
初识Java EE和Spring Boot
java·java-ee
AskHarries1 小时前
Java字节码增强库ByteBuddy
java·后端
佳佳_1 小时前
Spring Boot 应用启动时打印配置类信息
spring boot·后端
小灰灰__1 小时前
IDEA加载通义灵码插件及使用指南
java·ide·intellij-idea
夜雨翦春韭1 小时前
Java中的动态代理
java·开发语言·aop·动态代理
程序媛小果2 小时前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot