博客
关于我
Day243.JWT结合SpringSecurity -springsecurity-jwt-oauth2
阅读量:319 次
发布时间:2019-03-04

本文共 5194 字,大约阅读时间需要 17 分钟。

JWT与Spring Security认证鉴权详述

基于Session的应用开发缺陷

传统的B/S应用开发采用Session进行状态管理,通过cookie和sessionid实现用户状态的存取。这种方式在非浏览器客户端和集群应用中存在诸多问题,如无法自动维护cookie,且session不可共享。

JWT是什么

JWT(JSON Web Token)是一种用于传输用户身份信息的加密令牌,广泛应用于无状态的前后端分离应用开发。通过JWT,客户端无需存储任何状态信息,服务端仅需解签验证令牌即可获取用户信息。

JWT结构分析

JWT由三部分组成:Header(头)、Payload(有效载荷)和Signature(签名)。Header包含算法信息,Payload携带用户标识和其他自定义信息,Signature用于防止数据篡改。

JWT安全性

JWT的安全性依赖于密钥的保密性。通过HTTPS传输和定期更换密钥,可以有效提升JWT的安全性。服务端需妥善保护密钥,防止泄露。

Spring Security与JWT结合

认证流程

客户端通过用户名密码申请JWT,服务端验证后生成令牌并返回。Spring Security通过AuthenticationManager进行认证,生成JWT后返回给客户端。

接口鉴权

客户端将JWT放在HTTP请求头中,服务端通过 JwtRequestFilter 拦截请求,解签验证令牌,结合UserDetailsService获取用户信息,完成接口权限校验。

JWT工具类实现

开发JwtTokenUtil类,用于生成、解签、刷新JWT令牌。类似代码示例如下:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Map;
import java.util.Date;
@Data
@Component
public class JwtTokenUtil {
private String secret;
private Long expiration;
private String header;
public String generateToken(UserDetails userDetails) {
Map
claims = new HashMap<>();
claims.put("sub", userDetails.getUsername());
claims.put("created", new Date());
return generateToken(claims);
}
public String getUsernameFromToken(String token) {
Claims claims = getClaimsFromToken(token);
return claims.getSubject();
}
public Boolean isTokenExpired(String token) {
Claims claims = getClaimsFromToken(token);
return claims.getExpiration().before(new Date());
}
public String refreshToken(String oldToken) {
Claims claims = getClaimsFromToken(oldToken);
claims.put("created", new Date());
return generateToken(claims);
}
public Boolean validateToken(String token, UserDetails userDetails) {
String username = getUsernameFromToken(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
private String generateToken(Map
claims) {
Date expirationDate = new Date(System.currentTimeMillis() + expiration);
return Jwts.builder()
.setClaims(claims)
.setExpirationDate(expirationDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
private Claims getClaimsFromToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
claims = null;
}
return claims;
}
}

登录接口实现

定义JwtAuthController类,实现登录和令牌刷新接口。代码示例如下:

@RestController
public class JwtAuthController {
@Resource
private JwtAuthService jwtAuthService;
@PostMapping("/authentication")
public AjaxResponse login(@RequestBody Map
map) {
String username = map.get("username");
String password = map.get("password");
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
return AjaxResponse.error(new CustomException(
CustomExceptionType.USER_INPUT_ERROR, "用户名密码不能为空"));
}
try {
return AjaxResponse.success(jwtAuthService.login(username, password));
} catch (CustomException e) {
return AjaxResponse.error(e);
}
}
@PostMapping("/refreshtoken")
public AjaxResponse refresh(@RequestHeader("${jwt.header}") String token) {
return AjaxResponse.success(jwtAuthService.refreshToken(token));
}
}

接口鉴权过滤器

自定义JwtAuthenticationTokenFilter类,拦截请求解析JWT,校验用户权限,并通过Spring Security进行接口权限控制。代码示例如下:

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Resource
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String jwtToken = request.getHeader(jwtTokenUtil.getHeader());
if (jwtToken != null && !StringUtils.isEmpty(jwtToken)) {
String username = jwtTokenUtil.getUsernameFromToken(jwtToken);
if (username != null &&
SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = jwtTokenUtil.myUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null,
userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
}
filterChain.doFilter(request, response);
}
}

测试与常见问题

通过Postman测试登录接口获取JWT,测试成功后将令牌添加至HTTP请求头,访问受保护接口。确保权限配置正确,否则可能出现权限不足问题。

总结

JWT在无状态应用中提供了高效的身份验证方案,结合Spring Security,可实现细粒度的权限控制。虽然面临密钥保密等挑战,但通过合理配置和定期更新,可有效保障安全性。

转载地址:http://bpoq.baihongyu.com/

你可能感兴趣的文章