SpringBoot 集成JWT实现登录认证

本文介绍了JSON Web Token (JWT) 的基本概念及其在Spring Boot项目中的集成与使用方法。包括JWT的组成部分、如何在Spring Boot中引入JWT依赖、编写工具类获取Token、配置JWT拦截器、设置登录时的Token等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如果文章对你有帮助欢迎【关注❤️❤️❤️点赞👍👍👍收藏⭐⭐⭐】一键三连!一起努力!

一、JWT简介

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。

一个JWT实际上就是一个字符串,它由三部分组成,头部载荷签名

头部(Header)

头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。

这也可以被表示成一个JSON对象。

{"typ":"JWT","alg":"HS256"}

在头部指明了签名算法是HS256算法。 我们进行BASE64编码http://base64.xpcha.com/,编码后的字符串如下:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
载荷(playload)

载荷就是存放有效信息的地方

包含两种类型数据:

  • 标准中注册的声明的数据;
    • iss: jwt签发者
      sub: jwt所面向的用户
      aud: 接收jwt的一方
      exp: jwt的过期时间,这个过期时间必须要大于签发时间
      nbf: 定义在什么时间之前,该jwt都是不可用的.
      iat: jwt的签发时间
      jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

  • 自定义数据。
    • 存放我们想放在token中存放的key-value值

定义一个payload:

{"sub":"1234567890","name":"John Doe","admin":true}

然后将其进行base64加密,得到Jwt的第二部分。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
签证(signature)

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

header (base64后的)
payload (base64后的)
secret

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

将这三部分用.连接成一个完整的字符串,构成了最终的JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ 

二、Spring Boot 集成 JWT

1、引入项目依赖 pom.xml
	 <!--JWT-->
     <dependency>
         <groupId>com.auth0</groupId>
         <artifactId>java-jwt</artifactId>
         <version>3.10.3</version>
     </dependency>
2、编写对应工具类获取token
@Component
public class TokenUtils {

    public static String getToken(String userId, String sign) {
        return JWT.create().withAudience(userId) // 将 user id 保存到 token 里面,作为载荷
                .withExpiresAt(DateUtil.offsetHour(new Date(), 2)) //2小时后token过期
                .sign(Algorithm.HMAC256(sign)); // 以 password 作为 token 的密钥

    }
}
3、编写JWT拦截器
/**
 * @description: JWT拦截器
 * @author: Xiong
 * @date: 2022/11/17 21:41
 */
public class JwtInterceptor implements HandlerInterceptor {

    @Autowired
    IUserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从http请求头中取出token
        String token = request.getHeader("token");
        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return Boolean.TRUE;
        }
        // 执行认证
        if (StrUtil.isBlank(token)) {
            throw new ServiceException(HttpStatus.UNAUTHORIZED, "无token,请登录认证!");
        }
        // 获取token中的userId
        String userId;
        try {
            userId = JWT.decode(token).getAudience().get(0);
        } catch (JWTDecodeException j) {
            throw new ServiceException(HttpStatus.UNAUTHORIZED, "token验证失败,请重新登录!");
        }
        User user = userService.getById(userId);
        if (user == null) {
            throw new ServiceException(HttpStatus.UNAUTHORIZED, "用户不存在,请重新登录!");
        }
        // 用户密码加签验证token
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
        try {
            jwtVerifier.verify(token);
        } catch (JWTVerificationException e) {
            throw new ServiceException(HttpStatus.UNAUTHORIZED, "token验证失败,请重新登录!");
        }
        return Boolean.TRUE;
    }
}

4、注册拦截器
/**
 * @description: 注册拦截器
 * @author: Xiong
 * @date: 2022/11/17 21:51
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor())
                .addPathPatterns("/**") // 拦截所有请求,通过判断token决定是否需要登录
                .excludePathPatterns("/**/login", "/**/register", "/**/export", "/**/import", "/sys-file/**") // 放行的请求
        ;
    }

    /**
     * 注入到spring容器中
     * @return
     */
    @Bean
    public JwtInterceptor jwtInterceptor() {
        return new JwtInterceptor();
    }
}

5、登录时设置token
 @Override
    public UserDto login(UserDto userDto) {
        User one = getUser(userDto);
        if (one != null) {
            BeanUtil.copyProperties(one, userDto, true);
            // 设置token
            String token = TokenUtils.getToken(one.getUserId().toString(), one.getPassword());
            userDto.setToken(token);
            return userDto;
        } else {
            throw new ServiceException(HttpStatus.NO_CONTENT, "用户名或密码不正确");
        }
    }

对应实体类:

@Data
public class UserDto {
    private String username;
    private String password;
    private String nickname;
    private String avatar;
    private String token;
}
/**
 * @description:
 * @author: Xiong
 * @date: 2022/10/30 20:12
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "sys_user")
public class User extends BaseEntity{

    /**
     * 用户ID
     */
    @TableId(type = IdType.AUTO)
    private Long userId;

    /**
     * 部门ID
     */
    @ExcelExport("部门ID")
    @ExcelImport("部门ID")
    private Long deptId;

    /**
     * 用户账号
     */
    @ExcelExport("用户账号")
    @ExcelImport("用户账号")
    private String userName;

    /**
     * 用户昵称
     */
    @ExcelExport("用户昵称")
    @ExcelImport("用户昵称")
    private String nickName;

    /**
     * 用户邮箱
     */
    @ExcelExport("用户邮箱")
    @ExcelImport("用户邮箱")
    private String email;

    /**
     * 手机号码
     */
    @ExcelExport(value = "手机号码")
    @ExcelImport(value = "手机号码", maxLength = 11)
    private String phonenumber;

    /**
     * 用户性别
     */
    @ExcelExport(value = "用户性别", kv = "0-男;1-女;2,未知")
    @ExcelImport(value = "用户性别", kv = "0-男;1-女;2,未知")
    private String sex;

    /**
     * 用户头像
     */
    private String avatar;

    /**
     * 密码
     * 前端不返回
     */
    @JsonIgnore
    private String password;

    /**
     * 帐号状态(0正常 1停用)
     */
    private String status;

    /**
     * 删除标志(0代表存在 2代表删除)
     */
    private String delFlag;

    /**
     * 最后登录IP
     */
    private String loginIp;

    /**
     * 最后登录时间
     */
    private Date loginDate;

}

/**
 * @description: 基础数据
 * @author: Xiong
 * @date: 2022/10/30 20:14
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BaseEntity implements Serializable {

    /**
     * 创建者
     */
    private String createBy;

    /**
     * 创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;

    /**
     * 更新者
     */
    private String updateBy;

    /**
     * 更新时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;

    /**
     * 备注
     */
    private String remark;

}

启动项目后验证:

在这里插入图片描述

无法获取数据,并且提醒需要登录!

登录后再请求:

在这里插入图片描述

简易版登录认证实现!!!

感谢各位家人的观看喜欢的话点帮忙点赞👍👍👍哦

Spring Boot中,可以使用以下步骤将JWT集成到应用程序中以实现token认证: 1. 添加依赖项 在pom.xml文件中添加以下依赖项: ``` <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> ``` 2. 创建Token工具类 创建一个JwtTokenUtil工具类,该类将用于生成和验证JWT令牌。以下是一个基本的JwtTokenUtil类: ```java public class JwtTokenUtil { private static final String SECRET_KEY = "secret"; public static String generateToken(String username) { Date now = new Date(); Date expiryDate = new Date(now.getTime() + 3600000); return Jwts.builder() .setSubject(username) .setIssuedAt(now) .setExpiration(expiryDate) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .compact(); } public static String getUsernameFromToken(String token) { return Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody() .getSubject(); } public static boolean validateToken(String token) { try { Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token); return true; } catch (SignatureException ex) { System.out.println("Invalid JWT signature"); } catch (MalformedJwtException ex) { System.out.println("Invalid JWT token"); } catch (ExpiredJwtException ex) { System.out.println("Expired JWT token"); } catch (UnsupportedJwtException ex) { System.out.println("Unsupported JWT token"); } catch (IllegalArgumentException ex) { System.out.println("JWT claims string is empty."); } return false; } } ``` 3. 创建安全配置类 创建一个SecurityConfig类,该类将用于配置Spring Security以使用JWT进行认证。以下是一个基本的SecurityConfig类: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Autowired private JwtUserDetailsService jwtUserDetailsService; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder()); } @Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests().antMatchers("/authenticate").permitAll() .anyRequest().authenticated().and() .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } } ``` 4. 创建用户详细信息服务类 创建一个JwtUserDetailsService类,该类将用于从数据库或其他存储中获取用户信息以进行身份验证。以下是一个基本的JwtUserDetailsService类: ```java @Service public class JwtUserDetailsService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 在这里获取用户信息并返回UserDetails对象 return null; } } ``` 5. 创建身份验证过滤器 创建一个JwtAuthenticationFilter类,该类将用于拦截所有请求,并进行JWT身份验证。以下是一个基本的JwtAuthenticationFilter类: ```java public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private JwtUserDetailsService jwtUserDetailsService; @Autowired private JwtTokenUtil jwtTokenUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { final String requestTokenHeader = request.getHeader("Authorization"); String username = null; String jwtToken = null; // JWT Token is in the form "Bearer token". Remove Bearer word and get only the Token if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) { jwtToken = requestTokenHeader.substring(7); try { username = jwtTokenUtil.getUsernameFromToken(jwtToken); } catch (IllegalArgumentException e) { System.out.println("Unable to get JWT Token"); } catch (ExpiredJwtException e) { System.out.println("JWT Token has expired"); } } else { logger.warn("JWT Token does not begin with Bearer String"); } // Once we get the token validate it. if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username); // if token is valid configure Spring Security to manually set authentication if (jwtTokenUtil.validateToken(jwtToken)) { UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); usernamePasswordAuthenticationToken .setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); // After setting the Authentication in the context, we specify // that the current user is authenticated. So it passes the // Spring Security Configurations successfully. SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); } } chain.doFilter(request, response); } } ``` 6. 创建身份验证控制器 创建一个JwtAuthenticationController类,该类将用于处理身份验证请求,生成JWT令牌并返回给客户端。以下是一个基本的JwtAuthenticationController类: ```java @RestController public class JwtAuthenticationController { @Autowired private AuthenticationManager authenticationManager; @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private JwtUserDetailsService jwtUserDetailsService; @RequestMapping(value = "/authenticate", method = RequestMethod.POST) public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtRequest authenticationRequest) throws Exception { authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword()); final UserDetails userDetails = jwtUserDetailsService .loadUserByUsername(authenticationRequest.getUsername()); final String token = jwtTokenUtil.generateToken(userDetails.getUsername()); return ResponseEntity.ok(new JwtResponse(token)); } private void authenticate(String username, String password) throws Exception { try { authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); } catch (DisabledException e) { throw new Exception("USER_DISABLED", e); } catch (BadCredentialsException e) { throw new Exception("INVALID_CREDENTIALS", e); } } } ``` 现在,您的Spring Boot应用程序已经集成JWT以进行身份验证。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zyyn_未来可期

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值