参考教程: JWT认证原理、流程整合springboot实战应用,前后端分离认证的解决方案!
创建JWTUtils工具类
@Componentpublic class JWTUtils { private static final String salt = "!hfuw3y*&"; /** * 生成token * @param map 传入的参数 如用户名,用户id等信息 * @return */ public static String getToken(Map<String,String> map){ Calendar instance = Calendar.getInstance(); instance.add(Calendar.DATE,7);//默认7天过期 //创建jwt builder JWTCreator.Builder builder = JWT.create(); //payload map.forEach((k,v)->{ builder.withClaim(k,v); }); //签名并设置过期时间 String token = builder.withExpiresAt(instance.getTime()) .sign(Algorithm.HMAC256(salt)); return token; } /** * 验证合法性并获取token信息,如果非法则直接抛异常 * @param token * @return */ public static DecodedJWT verifyToken(String token){ DecodedJWT verify = JWT.require(Algorithm.HMAC256(salt)).build().verify(token); return verify; } /** * 从jwt中获取userId */ public static String getUserIdFromToken(String token){ DecodedJWT verify = JWT.require(Algorithm.HMAC256(salt)).build().verify(token); return verify.getClaim("userId").asString(); }}
JWT使用
用户首次访问,请求数据库,进行验证
/** *登录 * @param request * @return */ @Override public UserLoginResponse login(UserLoginRequest request) { //提前设置错误的返回信息 UserLoginResponse response = new UserLoginResponse(); response.setUserId(Convert.toLong(0));//0表示登录失败 //状态码是用户名或密码不正确 response.setCode(StatusCode.USERORPASSWORD_ERRROR.getCode()); response.setMsg(StatusCode.USERORPASSWORD_ERRROR.getMessage()); try { //查询名字相同的用户 QueryWrapper<UserEntity> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("user_name",request.getUsername()); UserEntity user = userDao.selectOne(queryWrapper); //如果存在名字相同且密码相同的用户则返回 if(user!=null && user.getUuid()>0){ String md5Password = MD5Util.encrypt(request.getPassword()); //密码相同,设置响应信息 if (user.getUserPwd().equals(md5Password)) { response.setUserId(user.getUuid()); response.setCode(StatusCode.SUCCESS.getCode()); response.setMsg(StatusCode.SUCCESS.getMessage()); } } } catch (Exception e) { log.error("login:",e); return response; } return response; }
验证通过,服务端使用JWT生成token
- 调用上述登录功能,得到返回结果
- 若结果中的userId等于0,表示登录失败,返回账号或密码错误
- 否则登录成功。将userId填入payload对象中,调用JWTUtils的getToken方法生成令牌,并把令牌写入response中返回。
@RequestMapping("/auth") public ResponseData generateToken(AuthRequest authRequest){ log.info("用户名:[{}]",authRequest.getUserName()); log.info("密码:[{}]",authRequest.getPassword()); //获取用户传入的用户名和密码,封装成request对象 UserLoginRequest request = new UserLoginRequest(); request.setUsername(authRequest.getUserName()); request.setPassword(authRequest.getPassword()); UserLoginResponse response = userService.login(request); //进行登录 //0表示登录失败 if(response.getUserId()!=0) { Map<String, String> payload = new HashMap<>(); payload.put("userId", response.getUserId().toString()); //生成JWT的令牌 String token = JWTUtils.getToken(payload); response.setToken(token); return new ResponseUtil<UserLoginResponse>().setData(response); }else{ return new ResponseUtil<>().setErrorMsg("账号或密码错误"); } }
用户下次请求携带token,需要验签
1. 编写拦截器类,实现preHandle 方法
- 该方法在请求处理之前调用,适合用来验签
- 首先获取请求头中携带的token,然后调用JWTUtils的verifyToken方法进行验证。
- 若方法报错,则通过try-catch模块进行捕获,异常包括签名异常、token过期异常或算法不匹配异常等。
- 将异常状态记录到map中,转为json写入到响应报文中。
- 代码实现:```java
@Slf4j
@Component
public class JWTInteceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("被拦截到了"); //1.获取请求头中的token String token = request.getHeader("token"); //2.验证token Map<String,Object> map = new HashMap<>(); try { JWTUtils.verifyToken(token);//验证令牌 return true; } catch (SignatureVerificationException e) { //签名异常 map.put("msg", "无效签名"); }catch (TokenExpiredException e){ //token过期异常 map.put("msg", "token过期"); }catch (AlgorithmMismatchException e){ //算法不匹配异常 map.put("msg", "算法不匹配"); }catch (Exception e){ map.put("msg", "无效签名"); } map.put("state",false); //设置状态 //将map转为json String json = new ObjectMapper().writeValueAsString(map); response.setContentType("application/json;charset=UTF-8"); response.getWriter().println(json);//写到响应报文中 return false;
}
}
```
2. 编写拦截器配置类
@Configurationpublic class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { //原则上拦截所有请求,但需要配置忽略列表 registry.addInterceptor(new JWTInteceptor()) .addPathPatterns("/**") .excludePathPatterns("/auth") .excludePathPatterns("/user/register") .excludePathPatterns("/user/checkName") .excludePathPatterns("/bus); }}
3. 验签通过,放行请求
测试JWT的SSO单点登录
未携带token,直接访问不在忽略列表中的url

用户首次登录返回token


