SpringBoot集成JWT令牌详细说明
作者:刻苦的樊同学
一,传统Cookie+Session与JWT对比
1, 在传统的用户登录认证中,因为http是无状态的,所以都是采用session方式。用户登录成功,服务端会保证一个session,当然会给客户端一个sessionId,客户端会把sessionId保存在cookie中,每次请求都会携带这个sessionId。
2,cookie+session这种模式通常是保存在内存中,而且服务从单服务到多服务会面临的session共享问题,随着用户量的增多,开销就会越大。而JWT不是这样的,只需要服务端生成token,客户端保存这个token,每次请求携带这个token,服务端认证解析就可。
3, JWT方式校验方式更加简单便捷化,无需通过redis缓存,而是直接根据token取出保存的用户信息,以及对token可用性校验,单点登录,验证token更为简单。
二,springboot集成jwt
1,jwt的整合依赖
<!-- JWT依赖 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.7.0</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency>
2,jwt的自定义配置
server: port: 8080 spring: application: name: springboot-jwt # 自定义配置 config: jwt: # 加密密钥 secret: abcdefg1234567 # token有效时长 expire: 3600 # header 名称 header: token
3,编写JwtConfig
JwtConfig负责
- 生成token
- 获取token中的注册信息
- 验证token是否过期失效
- 获取token失效时间
- 从token中获取用户名
- 获取jwt发布时间
package com.ftx.jwt.config; /** * @author FanJiangFeng * @version 1.0.0 * @ClassName JwtConfig.java * @Description TODO * @createTime 2020年06月22日 15:43:00 */ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.Date; /** * JWT的token,区分大小写 */ @ConfigurationProperties(prefix = "config.jwt", ignoreUnknownFields = true) @Component public class JwtConfig { private String secret; private long expire; private String header; /** * 生成token * @param subject * @return */ public String createToken (String subject){ Date nowDate = new Date(); Date expireDate = new Date(nowDate.getTime() + expire * 1000);//过期时间 return Jwts.builder() .setHeaderParam("typ", "JWT") .setSubject(subject) .setIssuedAt(nowDate) .setExpiration(expireDate) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } /** * 获取token中注册信息 * @param token * @return */ public Claims getTokenClaim (String token) { try { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); }catch (Exception e){ // e.printStackTrace(); return null; } } /** * 验证token是否过期失效 * @param expirationTime * @return */ public boolean isTokenExpired (Date expirationTime) { return expirationTime.before(new Date()); } /** * 获取token失效时间 * @param token * @return */ public Date getExpirationDateFromToken(String token) { return getTokenClaim(token).getExpiration(); } /** * 获取用户名从token中 */ public String getUsernameFromToken(String token) { return getTokenClaim(token).getSubject(); } /** * 获取jwt发布时间 */ public Date getIssuedAtDateFromToken(String token) { return getTokenClaim(token).getIssuedAt(); } // --------------------- getter & setter --------------------- public String getSecret() { return secret; } public void setSecret(String secret) { this.secret = secret; } public long getExpire() { return expire; } public void setExpire(long expire) { this.expire = expire; } public String getHeader() { return header; } public void setHeader(String header) { this.header = header; } }
4,配置拦截器
在拦截器中对token进行验证。
package com.ftx.jwt.config; import io.jsonwebtoken.Claims; import io.jsonwebtoken.SignatureException; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author FanJiangFeng * @version 1.0.0 * @ClassName TokenInterceptor.java * @Description TODO * @createTime 2020年06月22日 15:47:00 */ @Component public class TokenInterceptor extends HandlerInterceptorAdapter { @Resource private JwtConfig jwtConfig ; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws SignatureException { /** 地址过滤 */ String uri = request.getRequestURI() ; if (uri.contains("/login")){ return true ; } /** Token 验证 */ String token = request.getHeader(jwtConfig.getHeader()); if(StringUtils.isEmpty(token)){ token = request.getParameter(jwtConfig.getHeader()); } if(StringUtils.isEmpty(token)){ throw new SignatureException(jwtConfig.getHeader()+ "不能为空"); } Claims claims = null; try{ claims = jwtConfig.getTokenClaim(token); if(claims == null || jwtConfig.isTokenExpired(claims.getExpiration())){ throw new SignatureException(jwtConfig.getHeader() + "失效,请重新登录。"); } }catch (Exception e){ throw new SignatureException(jwtConfig.getHeader() + "失效,请重新登录。"); } //该token可用,放行 return true; } }
注册拦截器到SpringMvc
package com.ftx.jwt.config; /** * @author FanJiangFeng * @version 1.0.0 * @ClassName WebConfig.java * @Description TODO * @createTime 2020年06月22日 15:53:00 */ import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.config.annotation.*; import javax.annotation.Resource; import java.util.List; @Configuration public class WebConfig implements WebMvcConfigurer { @Resource private TokenInterceptor tokenInterceptor ; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(tokenInterceptor).addPathPatterns("/**"); } }
5,编写测试controller接口
package com.ftx.jwt.controller; import com.alibaba.fastjson.JSONObject; import com.ftx.jwt.config.JwtConfig; import com.ftx.jwt.util.ResultTool; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; /** * @author FanJiangFeng * @version 1.0.0 * @ClassName TokenController.java * @Description TODO * @createTime 2020年06月22日 16:05:00 */ @RestController public class TokenController { @Resource private JwtConfig jwtConfig ; /** * 用户登录接口 * @param userName * @param passWord * @return */ @PostMapping("/login") public JSONObject login (@RequestParam("userName") String userName, @RequestParam("passWord") String passWord){ JSONObject json = new JSONObject(); /** 验证userName,passWord和数据库中是否一致,如不一致,直接return ResultTool.errer(); 【这里省略该步骤】*/ // 这里模拟通过用户名和密码,从数据库查询userId // 这里把userId转为String类型,实际开发中如果subject需要存userId,则可以JwtConfig的createToken方法的参数设置为Long类型 String userId = 5 + ""; String token = jwtConfig.createToken(userId) ; if (!StringUtils.isEmpty(token)) { json.put("token",token) ; } return ResultTool.success(json) ; } /** * 需要 Token 验证的接口 */ @PostMapping("/info") public JSONObject info (){ return ResultTool.success("info") ; } /** * 根据请求头的token获取userId * @param request * @return */ @GetMapping("/getUserInfo") public JSONObject getUserInfo(HttpServletRequest request){ String usernameFromToken = jwtConfig.getUsernameFromToken(request.getHeader("token")); return ResultTool.success(usernameFromToken) ; } }
用PostMan测试工具测试一下,访问登录接口,当对账号密码验证通过时,则返回一个token给客户端 说明:token是在请求头处,request.getHeader()得到token。
当直接去访问info接口时,会返回token为空的异常 当在请求头加上正确token时,则拦截器验证通过,可以正常访问到接口
当在请求头加入一个错误token,则会返回token失效的异常 接下来测试一下获取用户信息,因为这里存的subject为userId,所以直接返回上面写死的假数据5
三,知识点概述
1,@Resource注解
- @Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。
- @Autowired默认按类型装配(这个注解是属于spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) 3,@Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
2,@ConfigurationProperties注解
@ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;prefix = "xxx":配置文件中哪个下面的所有属性进行一一映射
到此这篇关于SpringBoot集成JWT令牌详细说明的文章就介绍到这了,更多相关SpringBoot集成JWT令牌内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!