springboot中整合JWT Token
1、pom.xml中引入
<!--jwt token-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2、application.yml中配置
jwt:
# 加密密钥
secret: jellymilk
# token有效时长 1小时:3600 1天:86400
expire: 3600
# header 名称
header: Authorization
#发行人
issuer: cbyzs
#忽略token验证的 请求
ignores:
- /login/token
- /static/**
3、JwtProperties属性配置bean
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
/**
* jwt配置参数信息
*/
@Getter
@Setter
@ConfigurationProperties(prefix = "jwt")
public class JwtProperties {
private String secret;//加密密钥
private Long expire; // token有效期 秒
private String header;// token 采用的http头,一般使用: Authorization
private String issuer;//发行人
private List<String> ignores; //忽略token的页面
}
JwtTokenIgnore 注解,请求忽略token验证
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface JwtTokenIgnore {
}
4、Token的web拦截器
import cn.cbyzs.ssdemo.common.exception.BusinessException;
import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
@Component
public class JwtTokenInterceptor extends HandlerInterceptorAdapter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
// 如果不是映射到方法直接通过
if(!(handler instanceof HandlerMethod)){
return true;
}
//controller的方法上标记了 JwtTokenIgnore 注解的方法,直接放行
HandlerMethod handlerMethod=(HandlerMethod)handler;
Method method=handlerMethod.getMethod();
if(method.isAnnotationPresent(JwtTokenIgnore.class)){
return true;//放行
}
JwtProperties props= jwtTokenUtil.getJwtProperties();
List<String> ignores =props.getIgnores();
String contextPath=request.getContextPath();
String uri=request.getRequestURI();
String path=uri.replaceFirst(contextPath, "").replaceAll("/+", "/");
//ignores 忽略的请求 ,放行
for (String pattern : ignores) {
if (antPathMatcher.match(pattern, path)) {
return true;//放行
}
}
String token = request.getHeader(props.getHeader());
if(StrUtil.isEmpty(token)){
throw new BusinessException(3000,"token不能为空");
}
Claims claims = jwtTokenUtil.parseJWT(token);
if(claims.getExpiration().before(new Date())){
throw new BusinessException(3000,"token已过期,请重新获取");
}
request.setAttribute("identityId", claims.getSubject());//设置用户凭证id
return true;
}
}
WebConfig 配置,添加注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private JwtTokenInterceptor tokenInterceptor ;
/**
* 配置全站允许CORS跨域访问
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*") //允许的来源域 如http://www.demo.com
.allowedMethods("GET", "POST","PUT","DELETE","PATCH")
.allowCredentials(false).maxAge(3600);
}
/**
* 配置web拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor)
.addPathPatterns("/**");
}
}
5、JwtTokenUtil 工具bean
import cn.cbyzs.ssdemo.common.exception.BusinessException;
import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Base64;
import java.util.Date;
@Configuration
@EnableConfigurationProperties(JwtProperties.class)
@Slf4j
public class JwtTokenUtil {
@Autowired
private JwtProperties jwtProperties;
public JwtProperties getJwtProperties() {
return jwtProperties;
}
public String getSecretKey(){
return Base64.getEncoder().encodeToString(jwtProperties.getSecret().getBytes());
}
/**
* 生成token
* @param subject 一般为用户名
* @return
*/
public String createToken (String subject){
String secret =jwtProperties.getSecret();//密钥
long expire= jwtProperties.getExpire();//过期失效
String issuer = jwtProperties.getIssuer();//发行人
Date nowDate = new Date();
Date expireDate = new Date(nowDate.getTime() + expire * 1000);//过期时间
return Jwts.builder()
.setHeaderParam("typ", "JWT")
.setSubject(subject)//主题
.setIssuer(issuer)//发行人
.setIssuedAt(nowDate) //发行时间
.setExpiration(expireDate)//过期时间
//.setClaims()
.signWith(SignatureAlgorithm.HS256, getSecretKey())// 签名部分
.compact();
}
/**
* 获取token中注册信息 (body信息)
* @param token
* @return
*/
public Claims parseJWT (String token) {
try {
if(StrUtil.isEmpty(token)){
throw new BusinessException(3000,"token不能为空");
}
String secret =getSecretKey();//密钥
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}catch (Exception e){
log.error(e.getMessage(),e);
throw new BusinessException(3000,"验证token失败");
}
}
/**
* 从http请求中获取http的token头
* @return
*/
public String getHttpToken(){
HttpServletRequest request = ((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
String token= request.getHeader(jwtProperties.getHeader());
return token;
}
public Claims getTokenClaims(){
String token = getHttpToken();
return parseJWT(token);
}
public String getTokenClaimsSubject(){
String token = getHttpToken();
Claims claims= parseJWT(token);
return claims.getSubject();
}
}
6、LoginController.java 登录token、刷新token、从token中获取用户信息
import cn.cbyzs.ssdemo.common.RetMsg;
import cn.cbyzs.ssdemo.common.jwt.JwtTokenUtil;
import cn.cbyzs.ssdemo.user.model.User;
import cn.cbyzs.ssdemo.user.service.LoginService;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/login")
public class LoginController {
@Autowired
private LoginService loginService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
/**
* 登录获取token
* @param username 用户名
* @param password 密码
* @return
*/
@RequestMapping("/token")
public RetMsg token(String username,String password){
User user= loginService.login(username,password);
if(user!=null){ //说明登录成功
String token= jwtTokenUtil.createToken(user.getUsername());
Claims claims= jwtTokenUtil.parseJWT(token);//jwt主体
Map<String,Object> data=new HashMap<>();
data.put("user",user);
data.put("token",token);
data.put("expire",claims.getExpiration());
return RetMsg.success(data);//下发token和用户信息
}
return RetMsg.failure("用户登录失败");
}
/**
* 刷新token 获取新的token
* @return
*/
@RequestMapping("/refreshToken")
public RetMsg refreshToken(){
String username= jwtTokenUtil.getTokenClaimsSubject();
//生成新的token
String newToken = jwtTokenUtil.createToken(username);
return RetMsg.success(newToken) ;
}
/**
* 获取用户信息,从token中获取到username
* @return
*/
@RequestMapping("/getUserInfo")
public RetMsg getUserInfo(){
String username= jwtTokenUtil.getTokenClaimsSubject();
User user= loginService.findUserByUsername(username);
return RetMsg.success(user);
}
}