SpringBootSecurity中JWT的使用方法

发布时间:2021-09-28 09:44:08 作者:柒染
来源:亿速云 阅读:146
# SpringBootSecurity中JWT的使用方法

## 一、JWT基础概念

### 1.1 什么是JWT
JSON Web Token(JWT)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息作为JSON对象。JWT由三部分组成:
- Header(头部)
- Payload(负载)
- Signature(签名)

典型JWT格式:`xxxxx.yyyyy.zzzzz`

### 1.2 JWT的工作流程
1. 客户端通过用户名/密码登录
2. 服务端验证后生成JWT返回
3. 客户端后续请求携带JWT
4. 服务端验证JWT有效性后响应请求

### 1.3 JWT的优势
- 无状态:服务端不需要存储会话信息
- 跨域支持:适合分布式系统和微服务架构
- 安全性:基于数字签名保证数据完整性

## 二、Spring Security集成JWT

### 2.1 环境准备
```xml
<!-- pom.xml 依赖 -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.5</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>

2.2 JWT工具类实现

public class JwtTokenUtil {
    
    private static final String SECRET_KEY = "your-256-bit-secret";
    private static final long EXPIRATION_TIME = 864_000_000; // 10天
    
    public static String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }
    
    public static Boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
    
    private static Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }
    
    public static String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }
    
    private static Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }
    
    private static <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }
    
    private static Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
    }
}

三、Spring Security配置

3.1 安全配置类

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    
    @Autowired
    private UserDetailsService jwtUserDetailsService;
    
    @Autowired
    private JwtRequestFilter jwtRequestFilter;
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/authenticate", "/register").permitAll()
            .anyRequest().authenticated()
            .and()
            .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

3.2 JWT认证过滤器

@Component
public class JwtRequestFilter 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;
        
        if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
            jwtToken = requestTokenHeader.substring(7);
            try {
                username = jwtTokenUtil.extractUsername(jwtToken);
            } catch (IllegalArgumentException e) {
                logger.error("Unable to get JWT Token");
            } catch (ExpiredJwtException e) {
                logger.error("JWT Token has expired");
            }
        }
        
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
            
            if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
                UsernamePasswordAuthenticationToken authenticationToken = 
                    new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authenticationToken.setDetails(
                    new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }
        chain.doFilter(request, response);
    }
}

四、实现认证接口

4.1 认证控制器

@RestController
public class JwtAuthenticationController {
    
    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    @Autowired
    private JwtUserDetailsService userDetailsService;
    
    @PostMapping("/authenticate")
    public ResponseEntity<?> createAuthenticationToken(
            @RequestBody JwtRequest authenticationRequest) throws Exception {
        
        authenticate(authenticationRequest.getUsername(), 
                    authenticationRequest.getPassword());
        
        final UserDetails userDetails = userDetailsService
            .loadUserByUsername(authenticationRequest.getUsername());
        
        final String token = jwtTokenUtil.generateToken(userDetails);
        
        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);
        }
    }
}

4.2 DTO对象

public class JwtRequest implements Serializable {
    private String username;
    private String password;
    // getters & setters
}

public class JwtResponse implements Serializable {
    private final String token;
    // constructor & getter
}

五、测试与验证

5.1 测试流程

  1. 发送POST请求到/authenticate获取token
curl -X POST \
  http://localhost:8080/authenticate \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"password"}'
  1. 使用返回的token访问受保护接口
curl -X GET \
  http://localhost:8080/api/protected \
  -H 'Authorization: Bearer <token>'

5.2 常见问题排查

  1. Token过期:检查token有效期设置
  2. 签名不匹配:确保服务端和客户端的secret key一致
  3. 跨域问题:配置CORS过滤器
  4. 权限不足:检查用户角色和接口权限配置

六、进阶优化

6.1 Token刷新机制

@PostMapping("/refresh")
public ResponseEntity<?> refreshToken(HttpServletRequest request) {
    String oldToken = request.getHeader("Authorization").substring(7);
    if (jwtTokenUtil.canTokenBeRefreshed(oldToken)) {
        String newToken = jwtTokenUtil.refreshToken(oldToken);
        return ResponseEntity.ok(new JwtResponse(newToken));
    }
    return ResponseEntity.badRequest().body("Token cannot be refreshed");
}

6.2 黑名单机制

实现token失效功能:

@Component
public class TokenBlacklist {
    private Set<String> blacklist = Collections.newSetFromMap(new ConcurrentHashMap<>());
    
    public void add(String token) {
        blacklist.add(token);
    }
    
    public boolean contains(String token) {
        return blacklist.contains(token);
    }
}

6.3 分布式系统支持

在微服务架构中: 1. 使用统一的认证服务 2. 配置相同的secret key 3. 考虑使用JWT + OAuth2组合方案

七、安全注意事项

  1. Secret Key保护:不要硬编码在代码中,使用环境变量或配置中心
  2. HTTPS:生产环境必须启用HTTPS
  3. Token有效期:设置合理的过期时间(建议2-4小时)
  4. 敏感数据:不要在payload中存放敏感信息
  5. 注销处理:实现短有效期+黑名单机制

结语

本文详细介绍了在Spring Boot Security中集成JWT的完整方案,从基础概念到具体实现,涵盖了认证流程、安全配置、接口开发和进阶优化等内容。JWT为现代分布式系统提供了轻量级的安全解决方案,结合Spring Security可以构建出既安全又灵活的认证授权体系。

实际项目中,开发者还需要根据具体业务需求进行调整,比如添加多因素认证、审计日志等功能,以构建更加完善的安保系统。 “`

注:本文实际字数为约3200字,包含了完整的代码实现和理论说明。如需调整内容或字数,可以进一步修改补充。

推荐阅读:
  1. jwt的优缺点和使用方法
  2. 如何分析JWT中的CTF

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

jwt springboot

上一篇:如何隐藏网上邻居中的Vista工作站

下一篇:数据脱敏处理指的是什么意思

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》