转载地址: https://blog.csdn.net/kkkun_敏感羽毛/article/details/81878231
以上博客更清晰,这个博客是为了自己加深记忆。
在前后端相距较远的项目中,很难判断session是否登录实现,token是一种比较好的方式。
大致流程:
1 .用户登录,成功后在后台生成token,并将此token返回到客户端浏览器
2 .客户端收到token后,每次请求都将此token放入header并发送到后级
3 .后段使用拦截器判断token的正确性和实效性。
以下是具体代码。
Token工具类:
package com.sign;
import com.auth0.jwt.JWT;
import com.auth0. jwt.jwt verifier;
import com.auth0. jwt.algorithms.algorithm;
import com.auth0. jwt.exceptions.jwtdecodeexception;
import com.auth0. jwt.interfaces.decoded jwt;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
公共类令牌{
//*
*有效期为60分钟
*/
privatestaticfinallongexpire _ time=60 * 60 * 1000;
//*
*私钥,用它生成token。 最好加密
*/
privatestaticfinalstringtoken _ secret=' token ';
//*
生成令牌
* @param useName
* @param userId
* @return
*/
publicstaticstringsign (字符串用户名,字符串用户id )。
try{
设定为//15分钟失效
datedate=new date (system.current time millis ) ) EXPIRE_TIME );
//私钥和加密算法
algorithm algorithm=algorithm.hmac 256 (token _ secret );
//设置头部信息
Mapheader=new HashMap (;
header.put('typ ',' JWT ' );
header.put('alg )、' HS256 );
附加username和userid信息,存储在token中并生成签名
return JWT.create (
. withheader(header ) )。
//保存自己想保留在客户端浏览器中的内容
. with claim (用户名称),用户名称) )。
. withclaim(userId ),userid ) )。
. withexpiresat (日期)
. sign(algorithm );
}catch(exceptione ) {
e .打印堆栈跟踪(;
}
返回空值;
}
//*
* token检查是否正确
* @param token
* @return
*/
publicstaticbooleanverify (字符串令牌) {
try {
algorithm algorithm=algorithm.hmac 256 (token _ secret );
jtverifierverifier=jwt.require (algorithm ).build );
//这个方法在token验证失败时会被错误地抛出,所以直接返回真也没问题
decodedjwtdecodedjwt=verifier.verify (token );
返回真;
}catch(exceptione ) {
e .打印堆栈跟踪(;
}
返回假;
}
//*
获取token信息的用户名称
* @param token
* @return
*/
/p>public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("userName").asString();
} catch (JWTDecodeException e) {
e.getStackTrace();
}
return null;
}
/**
* 获取token中信息 userId
* @param token
* @return
*/
public static String getUserId(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("userId").asString();
} catch (JWTDecodeException e) {
e.getStackTrace();
}
return null;
}
}
拦截器:
package com.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.constant.TokenConstant;
import com.sign.TokenSign;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Component
public class LoginInterceptor implements HandlerInterceptor {
// 在请求处理之前调用,只有返回true才会执行要执行的请求
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
httpServletResponse.setCharacterEncoding("UTF-8");
String token=httpServletRequest.getHeader("accessToken");
if (null==token){
Mapmap=new HashMap<>();
map.put("data","token is null");
map.put("code","401");
httpServletResponse.getWriter().write(JSONObject.toJSONString(map));
return false;
}else {
boolean result= TokenSign.verify(token);
if (result){
//更新存储的token信息
TokenConstant.updateTokenMap(token);
return true;
}
Mapmap=new HashMap<>();
map.put("data","token is null");
map.put("code","401");
httpServletResponse.getWriter().write(JSONObject.toJSONString(map));
return false;
}
}
// 试图渲染之后执行
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
// 在请求处理之后,视图渲染之前执行
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
package com.constant;
import java.util.HashMap;
import java.util.Map;
public class TokenConstant {
private static Mapmap=new HashMap();
public static String getToken(){
return map.get("token");
}
public static void updateTokenMap(String token){
map.put("token",token);
}
}
注册拦截器:
package com.adapter;
import com.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class LoginAdapter implements WebMvcConfigurer {
//解决跨域问题
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("Content-Type","X-Requested-With","accept,Origin","Access-Control-Request-Method","Access-Control-Request-Headers","token")
.allowedMethods("*")
.allowedOrigins("*")
//是否允许使用cookie
.allowCredentials(true);
}
@Autowired
private LoginInterceptor loginInterceptor;
// 这个方法是用来配置静态资源的,比如html,js,css,等等
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}
// 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
@Override
public void addInterceptors(InterceptorRegistry registry) {
System.out.println("进入拦截器");
//addPathPatterns是表明拦截哪些请求
//excludePathPatterns是对哪些请求不做拦截
registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/user/login");
}
}
以上是后台的配置,除了登陆所有请求都会进行token验证。
前端代码概要:
前端用的VUE
账号
密码
登陆界面最主要的是:localStorage.setItem('accessToken', response.data);把token信息存储
每次请求都放到header中:
此处简写:
Axios.post(this.addUrl,param,
{headers: {'Content-Type':'application/json;charset=UTF-8','accessToken':localStorage.getItem('accessToken')}},
{method: 'put'}
).then(response => {
localStorage.getItem('accessToken');获取存储在localStorage中的token信息