这个是我工作中遇到的一个问题,因为之前没用过token,所以很陌生。不过好在我在网上一顿查,CSDN和度娘都快被我翻没了终于把他整出来了,现在来看一下代码。
首先先看一下我们用到得类有哪些。我们主要用到的是这几个类。
首先我们需要整一个TokenUtil的工具类
TokenUtil(工具类)
这里面的东西你基本上复制一下就可以不需要改什么东西,只需要改一下你的参数就可以了。
package com.wl.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.*;
/**
* @author 小于小于, 一条咸鱼
* @date 2022/6/17 13:37
*/
public class TokenUtil {
/**
* 过期时间为一个小时
*/
private static final long EXPIRE_TIME = 60 * 60 * 1000;
/**
* token私钥 这里的密码随便写就可以
*/
private static final String TOKEN_SECRET = "adm2345dfin234sdsdff";
/**
这里的参数根据你的需要来,因为我没有实体类,所以我在登录接口中写的假数据,这里你如果有实体类
的话你可以传一个User user
*/
public static String createToken(String username,String password) {
String token = null;
Date expiration = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Map<String,Object> header = new HashMap<>();
header.put("typ","JWT");
header.put("alg","HS256");
token = JWT.create()
.withIssuer("auth0")
.withHeader(header)
//这个地方是你传的参数
.withClaim("username",username)
.withClaim("password",password)
.withExpiresAt(expiration)
.sign(Algorithm.HMAC256(TOKEN_SECRET));
return token;
}
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("过期时间:" + jwt.getExpiresAt());
return true;
} catch (Exception e) {
e.printStackTrace();
System.out.println("Token认证失败,需要重新登录!");
}
return false;
}
}
接下来我们需要创建一个拦截器
创建拦截器(TokenInterceptor)
这里也不需要改很多东西直接,就是要改一下前端Vue的显示token的字段。
package com.wl.config;
import com.wl.util.TokenUtil;
import org.json.JSONObject;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author 小于小于, 一条咸鱼
* @date 2022/6/20 17:27
*/
@Component
@CrossOrigin
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");
//这里是前端Vue放在请求头中的字段名 Authorization:token字符串
String token = request.getHeader("Authorization");
if (token != null){
boolean flag = TokenUtil.verify(token);
if (flag) {
System.out.println("验证成功:"+ token);
return true;
}
}
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
try{
JSONObject json = new JSONObject();
json.put("msg","token失效请重新登陆");
json.put("code","50000");
response.getWriter().append(json.toString());
System.out.println("认证失败,未通过拦截器");
}catch (Exception e){
e.printStackTrace();
response.sendError(500);
return false;
}
return false;
}
}
配置跨域
这里的话也不需要改很多,只需要改一下你要拦截的路径即可,我是因为没有注册,我只把登录的接口给放出来了,其他的接口全部拦截。你可以根据的你的需要进行取舍,这里得线城池我是手动创建的,具体啥用我也没太搞懂,有没有这个应该是都可以的,你要嫌麻烦直接copy是没问题的。是
package com.wl.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author 小于小于, 一条咸鱼
* @date 2022/6/20 17:57
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
//最大30线程,外加30队列;【拒绝策略AbortPolicy(默认): 丢弃任务并抛出异常】
private static ExecutorService executorService = new ThreadPoolExecutor
(1, 30, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3));
@Autowired
private TokenInterceptor tokenInterceptor;
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("*")
.allowedMethods("*")
.allowedOriginPatterns("*")
.allowCredentials(true);
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setTaskExecutor(new ConcurrentTaskExecutor(executorService));
configurer.setDefaultTimeout(30000);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
List<String> excludePath = new ArrayList<>();
//排除拦截,除了注册登录(此时还没token),其他都拦截
excludePath.add("/user/login"); //登录
//excludePath.add("/login"); //注册
//excludePath.add("/assets"); //静态资源
//excludePath.add("/song/**"); //静态资源
registry.addInterceptor(tokenInterceptor)
.addPathPatterns("/**")
.excludePathPatterns(excludePath);
WebMvcConfigurer.super.addInterceptors(registry);
}
}
最后就是我们的登录接口了
LoginController
这里就很简单了,判断前端的账号密码是否正确,正确的话就创建Token然后保存在map中并返回给前端,如果不正确就给前端一个500的错误提示。
package com.wl.conrtoller;
import com.wl.util.TokenUtil;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
/**
* @author 小于小于, 一条咸鱼
* @date 2022/6/17 13:05
*/
@ResponseBody
@RestController
@CrossOrigin
@RequestMapping(value = "/user")
public class LoginController {
/**
* 登录
*
* @param
* @return
* 192.168.124.39:8081/user/login
*/
@PostMapping("/login")
public Object login(@RequestParam("username") String username,@RequestParam("password") String password){
Map<String, Object> map = new HashMap<>();
if ("admin".equals(username)&& "123456".equals(password)){
//创建token并返回给前端。
String token = TokenUtil.createToken(username, password);
map.put("token",token);
map.put("username",username);
map.put("password",password);
map.put("msg","登录成功");
map.put("code",200);
}else {
map.put("msg","账号密码错误");
map.put("code",500);
}
return map;
}
}