Springboot整合JWT做登入攔截
阿新 • • 發佈:2019-01-05
為什麼需要JWT
在傳統的 session 驗證中,服務端必須儲存 session ID,用於與使用者傳過來的 cookie 驗證。而在一開始儲存 session ID 時, 只會儲存在一臺伺服器上,所以只能由一個 server 應答,就算其他伺服器有空閒也無法應答,因此也利用不到分散式伺服器的優點。
而 JWT 依賴的是在客戶端本地儲存驗證資訊,不需要利用伺服器儲存的資訊來驗證,所以任意一臺伺服器都可以應答,伺服器的資源也被較好地利用。
spring整合JWT
- 新增JWT依賴
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9 .1</version>
</dependency>
- 新增JWT配置
audience:
clientId: 098f6bcd4621d373cade4e832627b4f6
base64Secret: MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY=
name: restapiuser
expiresSecond: 172800
- 配置JWT實體類
@Component
@ConfigurationProperties(prefix = "audience")
public class Audience {
private String clientId;
private String base64Secret;
private String name;
private int expiresSecond;
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getBase64Secret () {
return base64Secret;
}
public void setBase64Secret(String base64Secret) {
this.base64Secret = base64Secret;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getExpiresSecond() {
return expiresSecond;
}
public void setExpiresSecond(int expiresSecond) {
this.expiresSecond = expiresSecond;
}
}
- 使用攔截器配置JWT
@Component
public class JWTInterceptor implements HandlerInterceptor {
@Autowired
private Audience audience;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
final String authHeader = request.getHeader("authorization");
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
return true;
} else {
if (null == authHeader || !authHeader.startsWith("bearer;")) {
throw new ServiceRuntimeException(new ErrorDetails(1004));
}
}
final String token = authHeader.substring(7);
try {
final Claims claims = JwtHelper.parseJWT(token, audience.getBase64Secret());
if (claims == null) {
throw new ServiceRuntimeException(new ErrorDetails(1004));
}
request.setAttribute("CLAIMS", claims);
return true;
} catch (final Exception e) {
throw new ServiceRuntimeException(new ErrorDetails(1004));
}
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
}
}
- 配置攔截器
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Bean
JWTInterceptor jwtInterceptor(){
return new JWTInterceptor();
}
//spring攔截器載入在springcontentText之前,所以這裡用@Bean提前載入。否則會導致過濾器中的@AutoWired注入為空
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor()).addPathPatterns("/manage/**")
.excludePathPatterns(Arrays.asList("/", "/login"));
}
}
- 登入返回token
String jwtToken = JwtHelper.createJWT(admin.getAdminUsername(), admin.getAdminId(), audience.getClientId(),
audience.getName(), audience.getExpiresSecond()*1000, audience.getBase64Secret());
jwtToken = "bearer;" + jwtToken;
- jwt工具類
public class JwtHelper {
/**
* 解析jwt
*/
public static Claims parseJWT(String jsonWebToken, String base64Security){
try{
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(base64Security))
.parseClaimsJws(jsonWebToken).getBody();
return claims;
}catch (Exception e){
return null;
}
}
/**
*
* @param name
* @param userId
* @param audience
* @param issuer
* @param TTLMillis
* @param base64Security
* @return
*/
public static String createJWT(String name, Integer userId,
String audience, String issuer, long TTLMillis, String base64Security){
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
//生成簽名金鑰
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(base64Security);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
//新增構成JWT的引數
JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT")
.claim("unique_name", name)
.claim("userid", userId)
.setIssuer(issuer)
.setAudience(audience)
.signWith(signatureAlgorithm, signingKey);
//新增Token過期時間
if (TTLMillis >= 0) {
long expMillis = nowMillis + TTLMillis;
Date exp = new Date(expMillis);
builder.setExpiration(exp).setNotBefore(now);
}
//生成JWT
return builder.compact();
}
}