Spring boot + JWT 实现安全验证 ---jjwt
JWT只是一个标准。可以通过不过的开发语言实现,包括Java,.NET, Python,Node Js, JavaScript,Perl, Ruby,Go等。
同一种语言,不同的开发者提供了多种实现库,以Java语言为例有java-jwt、?jose4j、nimbus-jose-jwt、jjwt。这里用了jjwt
1.在spring boot 项目中引入jwt依赖
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2.generate token and validation token
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.CompressionCodecs;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
@Component
public class JwtTokenUtil {
@Value("${jwt.issuer}")
private String ISSUER; // project-name
@Value("${jwt.audience}")
private String AUDIENCE; // web
@Value("${jwt.expires_in}")
private int EXPIRES_IN; // 30 30min
@Value("${jwt.header}")
private String AUTH_HEADER; // Authorization
private String secretKey = "project-name";
private SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
// generate token
public String generateToken(Map<String, Object> claimMaps) {
if (null != claimMaps) {
claimMaps.forEach((key, val) -> {
claimMaps.put(key, val.toString());
});
}
return Jwts.builder()
.setId(UUID.randomUUID().toString()) // JWT_ID
.compressWith(CompressionCodecs.GZIP) // 数据压缩方式
.setIssuer(ISSUER) // 签发者信息
.setSubject("system") // 说明
.setAudience(AUDIENCE) // 接收用户
.setIssuedAt(new Date(System.currentTimeMillis())) // 签发时间
.setExpiration(new Date(System.currentTimeMillis() + EXPIRES_IN * 1000 * 60)) // 过期时间戳
.addClaims(claimMaps) // claim信息 // 自定义属性
.signWith(SIGNATURE_ALGORITHM, secretKey) // 签名算法以及密匙
.compact();
}
// validation:check if the token has expired
public boolean isTokenExpiration(String token) {
Claims claims = this.getAllClaimsFromToken(token);
if (null == claims) {
return true;
}
if (null == claims.getExpiration()) {
return true;
} else {
return claims.getExpiration().before(new Date());
}
}
// retrieve subject from token
public String getSubjectFromToken(String token) {
Claims claims = this.getAllClaimsFromToken(token);
String subject = claims.getSubject();
return subject;
}
// retrieve Claim from token 通过map的key值获取自定义的内容
public String getClaimFromToken(String token) {
Claims claims = this.getAllClaimsFromToken(token);
String KEY = (String) claims.get("KEY");
return KEY;
}
// for retrieveing any information from token we will need the secret key
public Claims getAllClaimsFromToken(String token) {
Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
return claims;
}
}
3. 把token的生成和验证工具 添加到spring 拦截器中
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class TokenInterceptor extends HandlerInterceptorAdapter {
private JwtTokenUtil jwtTokenUtil ;
public TokenInterceptor(JwtTokenUtil jwtTokenUtil ) {
this.jwtTokenUtil = jwtTokenUtil ;
}
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String token = request.getHeader("token");
if (null == token) {
logger.error("token is null");
response.setStatus(403);
return false;
}
boolean isExpiration = jwtTokenUtil.isTokenExpiration(token);
if (isExpiration) {
logger.error("token is expiration");
response.setStatus(403);
return false;
}
String subject = jwtTokenUtil.getSubjectFromToken(token);
if (!("system").equals(subject)) {
logger.error("subject is not correct ");
response.setStatus(403);
return false;
}
return true;
}
}
4.配置拦截器到项目
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private JwtTokenUtil jwtTokenUtil ;
@Override
public void addInterceptors(InterceptorRegistry registry) {
List<String> excludePath = new ArrayList<>();
// 获取token 不要验证
excludePath.add("/api/generateToken");
registry.addInterceptor(new TokenInterceptor(JwtTokenUtil ))
.addPathPatterns("/**")
.excludePathPatterns(excludePath);
}
}
5.测试
用户先通过/api/generateToken获取合法的token
然后下次请求中 带着token参数就可以 获取资源