專案總結67:Springboot中使用JWT+Token鑑權示例
阿新 • • 發佈:2020-09-09
專案總結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