web后端-登录验证--使用JWT和拦截

62 阅读3分钟

一.登录接口

1.接口参数

请求路径:/login

请求参数:usernamepassword

反应参数:json格式的

"id": 1,

"username": "songjiang",

"name": "宋江",

"token": "..."

2.编写代码

1.请求参数和反应参数的实体

请求参数LoginDTO

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
public class LoginDTO {
    private String username;
    private String password;
}

反应参数LoginVO

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
public class LoginVO {
    private Integer id;
    private String username;
    private String password;
    private String token;
}

2.定义一个loginController

@RestController
@Slf4j
public class LoginController {
    @Autowired
    EmpService empService;

    @PostMapping("/login")
    public Result login(@RequestBody LoginDTO loginDTO){
        log.info("loginDTO:{}",loginDTO);
        LoginVO loginVO = empService.login(loginDTO);
        return Result.success(loginVO);
    }
}

3.实现login方法,并在登陆成功之后发放Token,然后封装入LoginVO

@Override
public LoginVO login(LoginDTO loginDTO) {
    //验证用户名和密码是否填写
    String username = loginDTO.getUsername();
    String password = loginDTO.getPassword();
    if(StrUtil.isEmpty(username) || StrUtil.isEmpty(password)) {
        throw new RuntimeException("用户名或密码不能为空");
    }
    // 验证用户名和密码是否正确
    Emp emp = empMapper.findByUsername(username);
    if(emp == null) {
        throw new CustomException("用户名不存在");
    }
    if(!StrUtil.equals(password, emp.getPassword())) {
        throw new CustomException("用户名或密码错误");
    }
    // 封装并发放Token
    Map<String,Object> map = new HashMap<>();
    map.put("id", emp.getId());
    map.put("username", emp.getUsername());
    String token = JwtUtils.generateJwt(map);
    return new LoginVO(emp.getId(), emp.getUsername(), emp.getPassword(), token);
}

二.使用令牌验证

1.生成JWT

1.在pom.xml导入依赖

<!-- JWT依赖-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

2.引入JwtUtils 并设置"密钥"和"JWT有效期"

public class JwtUtils {

    //签名密钥,用于对 JWT 进行签名和验证,在整个应用生命周期内保持不变。
    private static final String signKey = "SVRIRUlNQQ==";
    //JWT 令牌的过期时间,单位为毫秒,这里设置为 24 小时。
    private static final Long expire = 24*60*60*1000L;
    //生成加密的JWT的方法___generateJwt
    public static String generateJwt(Map<String,Object> claims){
        // 使用 Jwts 构建器创建 JWT 令牌
        String jwt = Jwts.builder()
                // 将传入的 claims 信息添加到 JWT 的负载中
                .addClaims(claims)
                // 使用 HS256 算法和签名密钥对 JWT 进行签名
                .signWith(SignatureAlgorithm.HS256, signKey)
                // 设置 JWT 的过期时间,当前时间加上预设的过期时间
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                // 生成最终的 JWT 令牌字符串
                .compact();
       //return JWT令牌
        return jwt;
    }
    //生成解密JWT的方法___parseJWT
    public static Claims parseJWT(String jwt){
        // 使用 Jwts 解析器解析 JWT 令牌
        Claims claims = Jwts.parser()
                // 设置签名密钥,用于验证 JWT 的签名
                .setSigningKey(signKey)
                // 解析 JWT 令牌并获取其主体部分
                .parseClaimsJws(jwt)
                .getBody();
        //return 解密得到的信息
        return claims;
    }
}

三.使用拦截器Interceptor

1.定义TokenInterceptor并在其中重写preHandle方法

@Component
public class TokenInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从请求头中获取 JWT Token
        String token = request.getHeader("token");

        // 如果 Token 为空,说明用户未登录,返回 401 未授权状态码
        if (token == null) {
            response.setStatus(401);
            return false;
        }

        try {
            // 使用 JwtUtils 工具类解析 JWT Token
            Claims claims = JwtUtils.parseJWT(token);
            // 从解析后的 Claims 中获取用户 ID
            Object id = claims.get("id");
            // 打印用户 ID
            System.out.println(id);
            // Token 验证通过,将请求传递给下一个过滤器或目标资源
            return true;
        } catch (Exception e) {
            // Token 解析失败,说明 Token 无效或已过期,返回 401 未授权状态码
            response.setStatus(401);
            return false;
        }
    }
}

2.配置拦截器

创建配置类WebConfig,并且实现WebMvcConfigurer接口

@Configuration
public class WebConfig implements WebMvcConfigurer {
    //拦截器对象
    @Autowired
    private TokenInterceptor tokenInterceptor;

    /**
     * 重写 WebMvcConfigurer 接口的 addInterceptors 方法,用于注册拦截器。
     * @param registry 拦截器注册器,通过该对象可以添加自定义拦截器并配置拦截规则
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册自定义的 Token 拦截器对象
        registry.addInterceptor(tokenInterceptor)
                // 设置拦截器的拦截路径,"/**" 表示拦截所有请求
                .addPathPatterns("/**")
                // 设置排除拦截的路径,"/login" 路径不会被拦截
                .excludePathPatterns("/login");
    }
}

关于拦截路径

微信截图_20250718010602.png