项目总结67:Springboot中使用JWT+Token鉴权示例
START
代码示例
pom文件
<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>
JwtUtils类:创建JWT,解析JWT
import io.jsonwebtoken.*; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; import java.util.Date; public class JwtUtils { public final static String JWT_KEY = "aaabbcccdd"; //1-创建JWT public static String createJWT(String id, String subject, Long ttlMillis){ Date now = new Date(); Date expireDate = new Date(now.getTime()+ttlMillis); SecretKey secretKey = generateKey(); JwtBuilder jwtBuilder = Jwts.builder() .setHeaderParam("typ", "JWT")// .setId(id) .setSubject(subject) .setIssuer("tangyujie") .setIssuedAt(now) .setExpiration(expireDate) .signWith(SignatureAlgorithm.HS256, secretKey); //jwtBuilder.compact() = base64UrlEncodedHeader + '.' + base64UrlEncodedBody + '.' + base64UrlSignature; return jwtBuilder.compact(); } //生成key private static SecretKey generateKey(){ byte[] encodeKey = Base64.getDecoder().decode(JWT_KEY); return new SecretKeySpec(encodeKey,0,encodeKey.length,"AES"); } //获取token中注册信息 public static Claims getTokenClaim(String token){ SecretKey secretKey = generateKey(); try { return Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(token) .getBody(); } catch (Exception e) { return null; } } //验证token是否过期 public static boolean isTokenExpired(Date expirationTime){ return expirationTime.before(new Date()); } }
WebAppConfig: 实现WebMvcConfigurer接口,作拦截请求
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; //WebMvcConfigurer是一个接口,提供很多自定义的拦截器,例如跨域设置、类型转化器等等 @Configuration public class WebAppConfig implements WebMvcConfigurer { @Autowired private TokenInterceptor tokenInterceptor; //静态资源处理(这里是开放了Swagger组件的鉴权,因项目而异) @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("swagger-ui.html") .addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("doc.html") .addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/"); registry.addResourceHandler("/swagger-resources/**") .addResourceLocations("classpath:/META-INF/resources/swagger-resources/**"); registry.addResourceHandler("/swagger/**") .addResourceLocations("classpath:/META-INF/resources/swagger*"); registry.addResourceHandler("/v2/**") .addResourceLocations("classpath:/META-INF/resources/v2/**"); } //拦截器:拦截或允许请求 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(tokenInterceptor) .addPathPatterns("/**") .excludePathPatterns("/login") .excludePathPatterns("/doc.html","/swagger-resources/**","/webjars/**","/v2/**","/swagger-ui.html","/","/error","/csrf","/favico*","/api-docs","swagger.json"); } }
TokenInterceptor类:拦截器
import io.jsonwebtoken.Claims; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.security.SignatureException; @Component public class TokenInterceptor extends HandlerInterceptorAdapter { private final Logger logger = LoggerFactory.getLogger(TokenInterceptor.class); //在方法被调用前执行。 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { logger.info("request start urL:{}",request.getRequestURL()); String token = request.getHeader("Authorization");//从header中获取Authorization //重新登陆判断 if(StringUtils.isEmpty(token)){ throw new SignatureException("登陆信息为空,或者失效,请重新登陆"); } Claims claims = JwtUtils.getTokenClaim(token);//从token中解析出Claims if(claims == null){ throw new SignatureException("登陆信息为空,或者失效,请重新登陆"); } if(JwtUtils.isTokenExpired(claims.getExpiration())){ throw new SignatureException("登陆信息过期,请重新登陆"); } String account = claims.getSubject();//从claims中获取subject request.setAttribute("account",account);//将数据放在request中 return true; } //在方法执行后调用 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { logger.info("request over uri:{}",request.getRequestURI()); } }
controller类:模拟接口请求
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.UUID; /*** *@description *@author viruser *@date 2020/9/8 20:04 */ @RestController @RequestMapping("") public class LoginController { //1-登陆(认证+生成token) @GetMapping("/login") public Object login( @RequestParam String account, @RequestParam String password, HttpServletResponse response){ //1- 根据帐号密码请求数据库,获取用户信息 //......模拟用户,将account放进accessToken中 //2- 生成jwt String accessToken = JwtUtils.createJWT(UUID.randomUUID().toString(), account, 7200000L); //3-在header中返回accessToken response.setHeader("accessToken",accessToken); return "success and accessToken: " + accessToken; } //2-查询(示例如何获取请求域中的数据) @GetMapping("/user") public String getUserInfo(HttpServletRequest request){ String account = String.valueOf(request.getAttribute("account"));////从request中取出数据 return "account: " + account; } }
END