1.JWT是什么 JSON WEB TOKEN?
就是通过一定规范来生成token,然后可以通过解密算法逆向解密token,这样就可以获取用户信息
2怎么用?
JWT格式组成 头部、负载、签名
1.header+payload+signature
头部:主要是描述签名算法
负载:主要描述是加密对象的信息,如用户的id等,也可以加些规范里面的东西,如iss签发者,exp 过期时间,sub 面向的用户
签名:主要是把前面两部分进行加密,防止别人拿到token进行base解密后篡改token
2.关于jwt客户端存储,这块后端接口返回后,由前端来实现
可以存储在cookie,localstorage和sessionStorage里面,后端接口返回后,前端存储
~~~~~~~~开始使用~~~~~~我是分隔线~~~~~~
3.pom.xml引入
<!-- JWT相关 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
4.代码 工具类,用来提供生成token和检验的方法
package net.wnn.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import net.wnn.model.LoginUser;
import java.util.Date;
@Slf4j
public class JWTUtil {
/**
* token 过期时间,正常是7天,方便测试我们改为70
*/
private static final long EXPIRE = 1000 * 60 * 60 * 24 * 7 * 10;
/**
* 加密的秘钥
*/
private static final String SECRET = "wnn.net666";
/**
* 令牌前缀
*/
private static final String TOKEN_PREFIX = "wnn1024";
/**
* subject
*/
private static final String SUBJECT = "wnn";
/**
* 根据用户信息,生成令牌
*
* @param loginUser
* @return
*/
public static String geneJsonWebToken(LoginUser loginUser) {
if (loginUser == null) {
throw new NullPointerException("loginUser对象为空");
}
String token = Jwts.builder().setSubject(SUBJECT)
//payload
.claim("head_img", loginUser.getHeadImg())
.claim("id", loginUser.getId())
.claim("name", loginUser.getName())
.claim("mail", loginUser.getMail())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.signWith(SignatureAlgorithm.HS256, SECRET).compact();
token = TOKEN_PREFIX + token;
return token;
}
/**
* 校验token的方法
*
* @param token
* @return
*/
public static Claims checkJWT(String token) {
try {
final Claims claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token.replace(TOKEN_PREFIX, "")).getBody();
return claims;
} catch (Exception e) {
log.info("jwt token解密失败");
return null;
}
}
}
5.登录成功后调用获取token
//登录成功,生成token TODO
LoginUser loginUser = new LoginUser();
BeanUtils.copyProperties(userDO,loginUser);
String token = JWTUtil.geneJsonWebToken(loginUser);
将获取到的token返回给前端小伙伴。
return JsonData.buildSuccess(token);
这是上面代码中,使用到的VO
package net.wnn.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@Data
public class LoginUser {
/**
* 主键
*/
private Long id;
/**
* 名称
*/
private String name;
/**
* 头像
*/
@JsonProperty("head_img")
private String headImg;
/**
* 邮箱
*/
private String mail;
}
6.使用拦截器获取token 然后解密验证是否登录 ,将登录后的用户信息全局传递
package net.wnn.interceptor;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import net.wnn.enums.BizCodeEnum;
import net.wnn.model.LoginUser;
import net.wnn.util.CommonUtil;
import net.wnn.util.JWTUtil;
import net.wnn.util.JsonData;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
public static ThreadLocal<LoginUser> threadLocal = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String accessToken = request.getHeader("token");
if(accessToken == null) {
accessToken = request.getParameter("token");
}
if(StringUtils.isNotBlank(accessToken)){
//不为空
Claims claims = JWTUtil.checkJWT(accessToken);
if(claims == null){
//未登录
CommonUtil.sendJsonMessage(response,JsonData.buildResult(BizCodeEnum.ACCOUNT_UNLOGIN));
return false;
}
long userId = Long.valueOf(claims.get("id").toString());
String headImg = (String)claims.get("head_img");
String name = (String)claims.get("name");
String mail = (String)claims.get("mail");
LoginUser loginUser = new LoginUser();
loginUser.setName(name);
loginUser.setHeadImg(headImg);
loginUser.setId(userId);
loginUser.setMail(mail);
//通过attribute传递用户信息
//request.setAttribute("loginUser",loginUser);
//通过threadLocal传递用户登录信息
threadLocal.set(loginUser);
return true;
}
CommonUtil.sendJsonMessage(response,JsonData.buildResult(BizCodeEnum.ACCOUNT_UNLOGIN));
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
7.配置放行路径,区分需要登录的地址和不需要登录的地址
package net.wnn.config;
import lombok.extern.slf4j.Slf4j;
import net.wnn.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@Slf4j
public class InterceptorConfig implements WebMvcConfigurer {
public LoginInterceptor loginInterceptor(){
return new LoginInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor())
//拦截的路径
.addPathPatterns("/api/user/*/**","/api/address/*/**")
//排查不拦截的路径
.excludePathPatterns("/api/user/*/send_code","/api/user/*/captcha",
"/api/user/*/register","/api/user/*/login","/api/user/*/upload");
}
}
8.其它方法中的使用。直接通过LoginInterceptor调用就可以获得对应的用户信息
LoginUser loginUser = LoginInterceptor.threadLocal.get();