JWT的全称为:JSON Web Token ,它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
JWT由三部分组成,它们之间用圆点(.)连接。这三部分分别是:
Header.Payload.Signature
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI0NDAxODQyMDAwMDAwMDAwMDAifQ.Q7bE35fu4ZrE8FHF9HJfwAEV_pAYOtzYGDw5EVAnGts
在不动产预约登记系统项目开发初期,我试图用session来保存暂时需要保存或者暂时生成的数据,例如保存用户的账号密码记住用户登录的状态、保存各行政区的预约号球数量、保存用户当天取消预约次数等。但是使用session应用于前后端分离项目存在一定的弊端,例如由于session是存在与服务器的物理内存中,所以在分布式系统中,这种方式将会失效、因为session认证本质基于cookie,由于基于Cookie,而cookie无法跨域,所以session的认证也无法跨域,对单点登录不适用。所以使用session来进行开发项目并可行。
在这时了解到了JWT。结合项目场景的使用,JWT能满足项目基本功能的开发,原因有一下几点:
com.auth0 java-jwt3.10.3
根据使用场景,需要将生成token的方法写到对应的类中。在开发不动产预约登记系统中,我将生成token的方法写在了用户的基类中:
public class Account {//用户信息 String id; String email; String username; String password; int cancelnum; public String getToken(Account account) { String token=""; token= JWT.create().withAudience(account.getId()) .sign(Algorithm.HMAC256(account.getPassword())); return token; } }
为了确保token的唯一性,需要将用户的id以及password写入到token中进行加密.
@Override public void addInterceptors(InterceptorRegistry registry) { registry .addInterceptor(authorizeInterceptor) .addPathPatterns("/**");// 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录 }
需要开放路径因为拦截器需要对全局路径进行扫描。
public class AuthorizeInterceptor implements HandlerInterceptor { @Resource UserMapper mapper; String token; public String getToken() { return token; } public void setToken(String token) { this.token = token; } @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception { String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token // 如果不是映射到方法直接通过 if(!(object instanceof HandlerMethod)){ return true; } HandlerMethod handlerMethod=(HandlerMethod)object; Method method=handlerMethod.getMethod(); //检查是否有passtoken注释,有则跳过认证 if (method.isAnnotationPresent(PassToken.class)) { PassToken passToken = method.getAnnotation(PassToken.class); if (passToken.required()) { return true; } } //检查有没有需要用户权限的注解 if (method.isAnnotationPresent(UserLoginToken.class)) { UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class); if (userLoginToken.required()) { // 执行认证 if (token == null) { throw new RuntimeException("无token,请重新登录"); } // 获取 token 中的 user id String email; try { email = JWT.decode(token).getAudience().get(0); } catch (JWTDecodeException j) { throw new RuntimeException("401"); } Account account = mapper.findAccountByEmail(email); if (account == null) { throw new RuntimeException("用户不存在,请重新登录"); } // 验证 token JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(account.getPassword())).build(); try { jwtVerifier.verify(token); } catch (JWTVerificationException e) { throw new RuntimeException("401"); } return true; } } return true; } @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 { } }
根据项目功能,需要在登录接口写入注解以进行生成token并返回。
@UserLoginToken @PostMapping("/login2")//登录接口 public Object login(@Pattern(regexp = EMAIL_REGEX) @Length(min = 10, max = 20) @RequestParam("email") String email, @Length(min = 6, max = 16) @RequestParam("password")String password,HttpSession session){ JSONObject jsonObject=new JSONObject(); Account account=service.login(email,password); if(account==null){ jsonObject.put("status",401); jsonObject.put("success",false); jsonObject.put("message","登录失败,账号或密码错误"); return jsonObject; }else { String token = account.getToken(account); jsonObject.put("status",200); jsonObject.put("success",true); jsonObject.put("Token", token); jsonObject.put("message", account); return jsonObject; } }
部分参考、引用:
https://blog.csdn.net/weixin_45070175/article/details/118559272