官网:https://jwt.io/
第一步,引入依赖
<!--JWT生成token工具--><dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.19.1</version></dependency>
第二步、生成token的方法和验证的方法
package com.tj.reggie.controller.utils;import com.auth0.jwt.JWT;import com.auth0.jwt.JWTCreator;import com.auth0.jwt.JWTVerifier;import com.auth0.jwt.algorithms.Algorithm;import com.auth0.jwt.exceptions.JWTVerificationException;import com.auth0.jwt.interfaces.DecodedJWT;import java.util.Calendar;import java.util.HashMap;import java.util.Map;public class JwtUtils { //签名,这里改成pubilc可以后面动态的改变SIGN密钥值 pubilc static final String SIGN = "sign-content"; /** * 生成token,header.payload.sign * * @return */ public static String gettoken(Map<String, Object> map) { Calendar instance = Calendar.getInstance(); instance.add(Calendar.DATE, 2); //默认2天过去 //创建JWT builder JWTCreator.Builder builder = JWT.create(); //payload map.forEach((k, v) -> { builder.withClaim(k, String.valueOf(v)); }); String token = builder //header .withHeader(map) //令牌的过期时间 .withExpiresAt(instance.getTime()) //sign签名 .sign(Algorithm.HMAC256(SIGN)); return token; } /** * 验证token * * @param token 返回DecodedJWT对象,包含token信息 * @return */ public static DecodedJWT checktoken(String token) { //创建验证对象 return JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token); }}
第三步,改写用户登录验证方法
/** * 用户登录 * * @param request * @param users * @return * @throws Exception */ @Override public R login(HttpServletRequest request, Users users) throws Exception { //1.将页面提交的密码password进行sha256加密 String password = Sha256Util.sha256(users.getPassword()); //2.根据页面提交的用户名userName查询数据库 LambdaQueryWrapper<Users> lqw = new LambdaQueryWrapper<>(); //条件对象 lqw.eq(Users::getUser_accout, users.getUser_accout()); //判断数据库里是否有相同的用户名 Users res = getOne(lqw); //查询一条记录 //3.如果没有查询到则返回登录失败结果 if (res == null) { return R.error("用户名不存在"); } //4.密码比对,如果不一致则返回登录失败结构 if (!res.getPassword().equals(password)) { return R.error("密码不正确"); } //5.查看员工状态,如果为已禁用,则返回用户禁用信息 if (res.getIsuse() == 1) { return R.error("账号已经被禁用"); } //更新登录时间 res.setTimein(LocalDateTime.now()); //更新登录的IP地址 res.setIpin(TjIpUtils.getIP(request)); updateById(res); //6.登录成功,生成JWT的令牌 HashMap<String, Object> payload = new HashMap<>(); payload.put("id", res.getId()); log.info("token写入的id:{}", res.getId()); String userAccout = null; //name有中文,使用url编码,不然后期解码就是乱码 userAccout = URLEncoder.encode(res.getUser_accout(), "UTF-8"); payload.put("userAccount", userAccout); //JWT生成token令牌 String token = JwtUtils.gettoken(payload); R success = R.success("登录成功"); success.setData(token); //追加其他的数据 appendOtherData(success, res); return success; }
第四步,改写doFilter拦截器
package com.tj.demo.filter;import com.alibaba.fastjson.JSON;import com.auth0.jwt.exceptions.AlgorithmMismatchException;import com.auth0.jwt.exceptions.SignatureVerificationException;import com.auth0.jwt.exceptions.TokenExpiredException;import com.auth0.jwt.interfaces.DecodedJWT;import com.tj.demo.utils.BaseContext;import com.tj.demo.utils.JwtUtils;import com.tj.demo.utils.R;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.util.AntPathMatcher;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/** * 检查用户是否已经完成登录 * "/*"是拦截所有访问 */@WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")@Slf4jpublic class LoginCheckFilter implements Filter { //路径匹配器,支持通配符 public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher(); @Autowired private RedisTemplate redisTemplate; @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; //转成http请求 HttpServletResponse response = (HttpServletResponse) servletResponse; //转成http响应 //1.获取本次请求的URI String requestURI = request.getRequestURI(); log.info("拦截到请求:{}", requestURI); //2.定义不需要处理的请求路径 String[] uris = new String[]{ "/users/login", "/users/logout", "/users/avatar/**", "/login.html", "/logout.html", "/img/**", "/favicon.ico", "/common/**", "/static/**", }; //3.判断本次请求是否需要处理 boolean check = check(uris, requestURI); if (check) { //不需要处理的情况 filterChain.doFilter(request, response); log.info("本次请求不需要处理:{}", requestURI); return; } //4.获取token,判断登录状态,如果已登录,则直接放行 String token = request.getHeader("Authorization"); String msg = null; response.setContentType("application/json;charset=UTF-8"); //5、验证Redis里是否缓存了此token黑名单(token不能为空否则会报错) if (token != null && redisTemplate.opsForValue().get(token) != null) { msg = "token已经注销,请重新登录!"; log.info(msg); //6.如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据 response.getWriter().write(JSON.toJSONString(R.logout(msg))); log.info("未登录,返回响应数据:{}", requestURI); return; } try { DecodedJWT decodedJWT = JwtUtils.checktoken(token); filterChain.doFilter(request, response); //当前请求的用户id Long userId = Long.valueOf(decodedJWT.getClaim("id").asString()); //当前线程上设置用户id,BaseContext是自己编写的类,注意一下 BaseContext.setUserId(userId); log.info("token验证通过,threadLocal线程缓存用户id为:{}", userId); return; //通过检测可以继续 } catch (SignatureVerificationException e) { //更改了SIGN签名字段 e.printStackTrace(); msg = "服务器强制下线,请重新登录!!"; log.info(e.getMessage()); } catch (TokenExpiredException e) { //token设置的过期时间到了 e.printStackTrace(); msg = "登录时间长超过了2天,为了帐号安全,请重新登录!"; log.info(e.getMessage()); } catch (AlgorithmMismatchException e) { //算法不一样,这个一般不会改的。 e.printStackTrace(); msg = "token算法不一致!"; log.info(e.getMessage()); } catch (NullPointerException e) { e.printStackTrace(); msg = "没有token密钥,无法访问服务器"; log.info(e.getMessage()); } catch (Exception e) { e.printStackTrace(); msg = "token签名无效,请重新登录"; log.info(e.getMessage()); } //6.如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据 response.getWriter().write(JSON.toJSONString(R.logout(msg))); log.info("未登录,返回响应数据:{}", requestURI); } /** * 路径匹配,检查本次请求是否需要放行 * * @param uris * @param requestURI * @return */ public boolean check(String[] uris, String requestURI) { for (String uri : uris) { boolean match = PATH_MATCHER.match(uri, requestURI); if (match) return true; } return false; }}