此篇文章的内容也是学习不久,终于到周末有时间码一篇文章分享知识追寻者的粉丝们,学完本篇文章,读者将对token类的登录认证流程有个全面的了解,能够动态搭建本身的登录认证过程;对小项目而已经是个轻量级的认证机制,符合开发需求;更多精彩原创内容关注公主号知识追寻者,读者的确定,就是对做者的创做的最大支持;java
JWT的token由三段信息构成的,将这三段信息文本用.
链接一块儿就构成了JWT字符串;web
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1ODI4OTc4NDUsInVzZXJuYW1lIjoienN6eHoifQ.vyiExkFWCCmQA3PFYL0jJfIiYGWubngqB0WcgmtHOxg
URL
,POST
参数或者在HTTP header
发送,数据量小,传输速度快Token
是以JSON
加密的形式保存在客户端的,因此JWT
是跨语言支持;项目构件以下算法
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
jwt工具类中有三个方法,分别是生成数字签名用于用户首次登录时发送jwt给客户端;其次是校验方法,用于拦截器拦截全部规则内的url,每一个请求都必须带有服务器发送的jwt,通过验证后才放行请求;最后一个得到用户名的方法用于查询密钥,在验证jwt时做为参数传入;spring
/* * * @Author lsc * <p> JWT工具类 </p> * @Param * @Return */ public class JwtUtil { // Token过时时间30分钟 public static final long EXPIRE_TIME = 30 * 60 * 1000; /* * * @Author lsc * <p> 校验token是否正确 </p> * @Param token * @Param username * @Param secret * @Return boolean */ public static boolean verify(String token, String username, String secret) { try { // 设置加密算法 Algorithm algorithm = Algorithm.HMAC256(secret); JWTVerifier verifier = JWT.require(algorithm) .withClaim("username", username) .build(); // 效验TOKEN DecodedJWT jwt = verifier.verify(token); return true; } catch (Exception exception) { return false; } } /* * * @Author lsc * <p>生成签名,30min后过时 </p> * @Param [username, secret] * @Return java.lang.String */ public static String sign(String username, String secret) { Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); Algorithm algorithm = Algorithm.HMAC256(secret); // 附带username信息 return JWT.create() .withClaim("username", username) .withExpiresAt(date) .sign(algorithm); } /* * * @Author lsc * <p> 得到用户名 </p> * @Param [request] * @Return java.lang.String */ public static String getUserNameByToken(HttpServletRequest request) { String token = request.getHeader("token"); DecodedJWT jwt = JWT.decode(token); return jwt.getClaim("username") .asString(); } }
实体中包含用户名,和密码,一切从简;数据库
/** * @Author lsc * <p> </p> */ @Data public class SysUser { private String username; private String password; }
表现层代码用户用户登录认证,认证成功后发放token给客户端;api
/** * @Author lsc * <p> </p> */ @RestController public class SysUserController { @PostMapping(value = "/login") public Map<String, Object> login(@RequestBody SysUser sysUser){ Map<String, Object> map = new HashMap<>(); String username = sysUser.getUsername(); String password = sysUser.getPassword(); // 省略 帐号密码验证 // 验证成功后发送token String token = JwtUtil.sign(username,password); if (token != null){ map.put("code", "200"); map.put("message","认证成功"); map.put("token", token); return map; } map.put("code", "403"); map.put("message","认证失败"); return map; } }
测试url http://localhost:8080/login跨域
postman post请求测试参数以下浏览器
{ "username": "zszxz", "password": "zszxz" }
返回内容以下springboot
{ "code": "200", "message": "认证成功", "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1ODI4OTc4NDUsInVzZXJuYW1lIjoienN6eHoifQ.vyiExkFWCCmQA3PFYL0jJfIiYGWubngqB0WcgmtHOxg" }
基于前面已经实现jwt登陆认证后发放token给客户端;本节内容就是将token放入请求头中发送请求给服务器;服务器使用拦截器拦截请求对token进行验证;验证成功请求经过,不然请求资源失败;服务器
自定义拦截器JwtInterceptor,实现HandlerInterceptor接口,每次请求到达以前都会验证token是否有效;
/** * @Author lsc * <p>token验证拦截器 </p> */ @Component public class JwtInterceptor implements HandlerInterceptor { @Autowired SysUserService sysUserService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 从 http 请求头中取出 token String token = request.getHeader("token"); // 若是不是映射到方法直接经过 if(!(handler instanceof HandlerMethod)){ return true; } if (token != null){ String username = JwtUtil.getUserNameByToken(request); // 这边拿到的 用户名 应该去数据库查询得到密码,简略,步骤在service直接获取密码 boolean result = JwtUtil.verify(token,username,sysUserService.getPassword()); if(result){ System.out.println("经过拦截器"); return true; } } return false; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
/** * @Author lsc * <p> 模拟查询数据库得到帐号密码 </p> */ @Service public class SysUserService { public String getPassword(){ return "zszxz"; } }
拦截器配置中主要定义拦截请求规则,将拦截器注入WebMvcConfigurer;cors跨域处理;
/* * * @Author lsc * <p>拦截器配置 </p> * @Param * @Return */ @Configuration public class InterceptorConfig implements WebMvcConfigurer { /* * * @Author lsc * <p> 设置拦截路径 </p> * @Param [registry] * @Return void */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authenticationInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/login"); } /* * * @Author lsc * <p> 将拦截器注入context </p> * @Param [] * @Return com.zszxz.jwt.interceptor.JwtInterceptor */ @Bean public JwtInterceptor authenticationInterceptor() { return new JwtInterceptor(); } /* * * @Author lsc * <p>跨域支持 </p> * @Param [registry] * @Return void */ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowCredentials(true) .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS", "HEAD") .maxAge(3600 * 24); } }
表现层接口用于拦截亲求测试
/** * @Author lsc * <p> </p> */ @RestController public class TestController { @GetMapping(value = "/api/test") public String get(){ return "zszxz"; } }
测试url http://localhost:8080/api/test
发送get请求给服务器,带有请求头,key为token,value为用户首次登录时返回的token串;
测试返回内容以下
zszxz
https://jwt.io/introduction/
源码 关注公主号或者做者专栏说明便可得到;