springBoot使用JWT实现单点登录
1.首先在项目的pom.xml中引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
2.封装一个TokenUtils
sign()方法传入用户的信息,使用JWT生成签名。
verify()方法用于验证签名,如果已失效,就返回false。
getUserId()用户获取token中的userId。
EXPIRE_TIME为token过期时间。
public class TokenUtils {
private static final long EXPIRE_TIME= 15*60*1000;
private static final String TOKEN_SECRET="tokenqkj"; //密钥盐
/**
* 签名生成
* @param user
* @return
*/
public static String sign(User user){
String token = null;
try {
Date expiresAt = new Date(System.currentTimeMillis() + EXPIRE_TIME);
token = JWT.create()
.withIssuer("auth0")
.withClaim("userId", user.getId())
.withClaim("userName",user.getName())
.withExpiresAt(expiresAt)
// 使用了HMAC256加密算法。
.sign(Algorithm.HMAC256(TOKEN_SECRET));
} catch (Exception e){
e.printStackTrace();
}
return token;
}
/**
* 签名验证,并返回用户的id
* @param token
* @return
*/
public static boolean verify(String token){
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).withIssuer("auth0").build();
DecodedJWT jwt = verifier.verify(token);
// System.out.println("认证通过:");
// System.out.println("issuer: " + jwt.getIssuer());
// System.out.println("userId: " + jwt.getClaim("userId").asString());
// System.out.println("过期时间: " + jwt.getExpiresAt());
return true;
} catch (Exception e){
return false;
}
}
/**
* 获取token中的userId
*/
public static String getUserId(String token){
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).withIssuer("auth0").build();
DecodedJWT jwt = verifier.verify(token);
return jwt.getClaim("userId").asString();
} catch (Exception e){
return null;
}
}
}
3.编写token拦截器
@Component
public class TokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception{
if(request.getMethod().equals("OPTIONS")){
response.setStatus(HttpServletResponse.SC_OK);
return true;
}
response.setCharacterEncoding("utf-8");
String token = request.getHeader("token");
if(token != null){
boolean result = TokenUtils.verify(token);
if(result){
// System.out.println("通过拦截器");
return true;
}
}
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = null;
try{
// JSONObject json = new JSONObject();
json.put("success","false");
json.put("msg","认证失败,未通过拦截器");
json.put("code","50000");
// Map<String,Object> resMap = new HashMap<>();
// resMap.put("code",50000);
// resMap.put("message","未登录或登录失败");
// response.getWriter().append(resMap.toString());
System.out.println("认证失败,未通过拦截器");
response.getWriter().write("50000");
}catch (Exception e){
e.printStackTrace();
response.sendError(500);
return false;
}
return false;
}
}
4.配置token拦截器
/**
* 拦截器配置
*/
@Configuration
public class IntercepterConfig implements WebMvcConfigurer {
@Autowired(required = true)
private TokenInterceptor tokenInterceptor;
//构造方法
public IntercepterConfig(TokenInterceptor tokenInterceptor){
this.tokenInterceptor = tokenInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry){
List<String> excludePath = new ArrayList<>();
// excludePath.add("/user_register"); //注册
excludePath.add("/user/login"); //登录
excludePath.add("/logout"); //登出
excludePath.add("/static/**"); //静态资源
excludePath.add("/swagger-ui.html/**"); //静态资源
excludePath.add("/assets/**"); //静态资源
excludePath.add("/default/**"); //静态资源
excludePath.add("/upload/**"); //静态资源
// excludePath.add("/**");
registry.addInterceptor(tokenInterceptor)
.addPathPatterns("/**")
.excludePathPatterns(excludePath);
WebMvcConfigurer.super.addInterceptors(registry);
}
/**
* 跨域支持
*
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS", "HEAD")
.maxAge(3600 * 24);
}
}
5.前端配置拦截器
后端登录成功后返回token,前端储存起来,在之后每次发请求的时候带上token,如果请求返回的结果为50000,那么就表示未通过拦截器,用户未登录或者登录已失效。
/**
* 用戶登录
*/
@RequestMapping(value = "login",method = RequestMethod.POST)
public ResponseResult login(User user){
//根据前端输入的登录名和密码查询是否由该用户
User queryUser = new User();
queryUser.setLoginName(user.getLoginName());
User queryUser1 = userService.getByEntity(queryUser);
if(queryUser1==null){
return new ResponseResult(ResponseCodeCanstants.FAILED,"没有该用户");
}else{
try {
queryUser.setPassword(MD5Utils.EncoderByMd5(user.getPassword()));
User queryUser2 = userService.getByEntity(queryUser);
if(queryUser2==null){
return new ResponseResult(ResponseCodeCanstants.FAILED,"用户名或者密码失败");
}
String token = TokenUtils.sign(queryUser2);
//定义结果集
// Map<String,Object> resultMap = new HashMap<>();
// resultMap.put("loginData", JSONArray.toJSON(queryUser2).toString());
// resultMap.put("token",token);
if(token!=null){
return new ResponseResult(ResponseCodeCanstants.SUCCESS,token,"登录成功");
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return new ResponseResult(ResponseCodeCanstants.FAILED,"登录出现错误");
}
前端代码
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
loading = Loading.service({
lock: true,
text: 'Loading',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.5)'
});
config.headers = {DeviceType: 'H5'};
if (localStorage.getItem('token')) {
config.headers.token = localStorage.getItem('token');
}
return config;
}, function (error) {
// 对请求错误做些什么
Message(error);
console.log("========>", error);
return Promise.reject(error);
});
之后每次发请求的时候就都会带上token了~