SpringSecurity+JWT
JWT认证和Session认证
参考资料:什么是 JWT – JSON WEB TOKEN - 简书 (jianshu.com)
JWT:JSON WEB TOKEN
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
传统Session认证存在的问题
因为http协议本身是无状态协议,即时用户第一次请求时提供了用户名和密码进行认证,第二次请求时仍需要提供用户名和密码,因为对后端来说并不能知道是哪个用户发出的请求。
传统的Session认证就是用户登录成功后,将用户信息存放在后台session中,前端下次请求时需要在cookie中携带对应的sessionid才能表明其已经登录过。
问题:
JWT认证方式
token的认证方式下,服务端不需要为用户保存认证信息
大致流程
- 用户账号密码请求服务器
- 服务器认证通过
- 生成一个token(jwt),该jwt是加密的,需要服务端有对于的私钥才能解密。
- 前端存储用户token,并在后续请求头中携带token
- 服务器验证token的值并解密取出token中的信息再次生成security中需要的authentication对象。
具体实现
因为Security的认证流程就是一串和UsernamePasswordFilter类似的过滤器在过滤器链上拦截请求然后认证,认证通过就返回认证信息即可,我们可以模拟UsernamePasswordFilter写一个自己的UsernamePasswordFilter
1. 配置JWT认证异常处理器
package com.sy.security.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sy.security.domain.pojo.Result;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JWTAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json; charset=utf-8");
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
String reason = "统一处理,原因:" + e.getMessage();
httpServletResponse.getWriter().write(new ObjectMapper().writeValueAsString(Result.failure(reason)));
}
}
2. 生成JWT的工具类
pom.xml中导入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
创建JwtTokenUtils类
package com.sy.security.util;
import com.sy.security.domain.pojo.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
@Slf4j
public class JwtTokenUtils {
public static final String TOKEN_HEADER = "Authorization";
public static final String TOKEN_PREFIX = "Bearer ";
private static final String SECRET = "xxx";
private static final String ISS = "Shenyang";
private static final long EXPIRATION = 7200L;
private static final long EXPIRATION_REMEMBER = 604800L;
public static String createToken(String subject,HashMap<String,Object> claims,boolean isRememberMe) {
long expiration = isRememberMe ? EXPIRATION_REMEMBER : EXPIRATION;
return Jwts.builder()
.signWith(SignatureAlgorithm.HS512, SECRET)
.setClaims(claims)
.setIssuer(ISS)
.setSubject(subject)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
.compact();
}
public static String getUsername(String token){
return getTokenBody(token).getSubject();
}
public static boolean isExpiration(String token) {
try {
return getTokenBody(token).getExpiration().before(new Date());
} catch (ExpiredJwtException e) {
return true;
}
}
private static Claims getTokenBody(String token){
return Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
}
public static Object getClaims(String token,String claim){
return getTokenBody(token).get(claim);
}
public static String createJwt(User user,Integer rememberMe) throws IOException {
boolean isRemember = rememberMe == 1;
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
String role = null;
for (GrantedAuthority authority : authorities) {
role = authority.getAuthority();
}
HashMap<String,Object> claims = new HashMap<>();
claims.put("USER_ROLE",role);
claims.put("USER_NAME",user.getUsername());
claims.put("USER_REAL_NAME",user.getRealName());
return JwtTokenUtils.createToken(user.getUsername(),claims , isRemember);
}
}
-
JWT异常类 package com.sy.security.handler.exception;
public class TokenIsExpiredException extends Exception {
public TokenIsExpiredException() {
}
public TokenIsExpiredException(String message) {
super(message);
}
public TokenIsExpiredException(String message, Throwable cause) {
super(message, cause);
}
public TokenIsExpiredException(Throwable cause) {
super(cause);
}
public TokenIsExpiredException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
|