学习记录:基于JWT简单实现登录认证功能-demo

0 阅读3分钟

背景

简单梳理一下项目中的JWT登录认证功能

一、简单理解

JWT(JSON Web Token)是json格式的网络令牌,分为三个主要部分:Header(头部)、Payload(载荷)和Signature(签名)。其对比cookie和session的优点,常用于登录认证场景中。

二、实现步骤

数据库设计

-- auto-generated definition
create table user
(
    id          int auto_increment comment ' 主键'
        primary key,
    username    varchar(20)  not null comment '用户名',
    password    varchar(100) not null comment '密码',
    create_time datetime     not null comment '创建时间',
    update_time datetime     not null comment '修改时间',
    constraint user_uk
        unique (username)
)
    comment '用户表';

项目基础配置

1、application.yml 配置

server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/my_blog_demo?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  configuration:
    map-underscore-to-camel-case: true

2、pom.xml依赖文件配置

<!--添加以下JWT所需的依赖-->

<!-- JWT API -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>

<!-- JWT 实现 -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

<!-- JSON 解析 -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

基础组件封装

1、实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    //用户id
    private Integer id;
    //用户名
    private String username;
    //密码
    private String password;
    //创建时间
    private LocalDateTime createTime;
    //修改时间
    private LocalDateTime updateTIme;
}

2、Result类

@Data
public class Result<T> implements Serializable {

    private Integer code; //业务状态码:1成功,0失败
    private String msg; //提示信息
    private T data; //数据

    private Result(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    //响应成功,无数据返回
    public static <T> Result<T> success() {
        return new Result<>(1, "success", null);
    }

    //响应成功,有数据返回
    public static <T> Result<T> success(T data) {
        return new Result<>(1, "success", data);
    }

    //响应失败,提示错误信息
    public static <T> Result<T> error(String msg) {
        return new Result<>(0, msg, null);
    }
}

3、DTO

@Data
public class UserLoginDTO {
    //用户名
    private String username;
    //密码
    private String password;
}

4、全局异常处理器

public class BaseException extends RuntimeException {
    public BaseException(String message) {
        super(message);
    }
}
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    @ExceptionHandler
    public Result exceptionHandler(BaseException ex){
        log.error("异常信息:{}", ex.getMessage());
        return Result.error(ex.getMessage());
    }
}

JWT认证实现

1、封装JWT工具类

public class JwtUtil {

    /**
     * 生成JWT
     */
    public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {

        Key key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));

        long expMillis = System.currentTimeMillis() + ttlMillis;
        Date exp = new Date(expMillis);

        JwtBuilder builder = Jwts.builder()
                .setClaims(claims)
                .setExpiration(exp)
                .signWith(key, SignatureAlgorithm.HS256);

        return builder.compact();
    }

    /**
     * 解析JWT
     */
    public static Claims parseJWT(String secretKey, String token) {

        Key key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));

        Claims claims = Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token)
                .getBody();

        return claims;
    }
}

2、定义拦截器并注册

@Slf4j
@Component
public class JwtInterceptor implements HandlerInterceptor {

    private static final String SECRET_KEY = "hooknum-login-demo-jwt-secret-key-2026";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从请求头获取token
        String token = request.getHeader("token");

        if (token == null || token.isEmpty()) {
            log.info("认证不通过");
            response.setStatus(401);
            return false;
        }

        try {
            // 解析token
            Claims claims = JwtUtil.parseJWT(SECRET_KEY, token);
            // 可以把用户信息放入request
            request.setAttribute("userId", claims.get("userId"));
        } catch (Exception e) {
            log.info("认证不通过");
            response.setStatus(401);
            return false;
        }

        return true;
    }
}
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private JwtInterceptor jwtInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor)
                .addPathPatterns("/**")          // 拦截所有请求
                .excludePathPatterns("/user/auth/login"); // 登录接口放行
    }
}

三层架构实现

Controller层

@RestController
@RequestMapping("/user/auth")
@Slf4j
public class UserAuthController {

    @Autowired
    private UserAuthService userAuthService;

    @PostMapping("/login")
    public Result login(@RequestBody UserLoginDTO userLoginDTO) {
        log.info("用户登录:");
        String token = userAuthService.login(userLoginDTO);
        return Result.success(token);
    }
}

Service层

public interface UserAuthService {
    String login(UserLoginDTO userLoginDTO);
}
@Service
public class UserAuthServiceImpl implements UserAuthService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public String login(UserLoginDTO userLoginDTO) {
        String username = userLoginDTO.getUsername();
        String password = userLoginDTO.getPassword();

        User user = userMapper.getByUseranme(username);

        //用户身份检验
        if (user == null){
            throw new BaseException("用户不存在");
        }
        if (!user.getPassword().equals(password)){
            throw new BaseException("密码错误");
        }

        //检验通过、生成JWT令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId",user.getId());
        String token = JwtUtil.createJWT("hooknum-login-demo-jwt-secret-key-2026", 3600000, claims);

        return token;
    }
}

Mapper层

@Mapper
public interface UserMapper {

    @Select("select * from my_blog_demo.user where username = #{username}")
    User getByUseranme(String username);
}

三、测试

先向表中插入一条用户数据,hooknum,123456

Snipaste_2026-03-10_09-59-01.png

Snipaste_2026-03-10_10-01-04.png

四、总结

本篇功能简单,设计所学知识有JWT、全局异常处理器、拦截器。一般JWT会配合ThreadLocal保存当前用户。通篇下来让我进一步复习了登录认证的流程和设计,不过实战登录认证功能有更加完善的规范设计和安全机制,这值得我进一步学习和实践。

最后

声明:本文仅作为学习记录分享使用,内容基于作者当前技术认知整理,如需要参考请自行决定价值度,若有不准确或描述不清之处,欢迎评论区指正,我会在能力范围内修正完善,谢谢阅读!🙏