1. 程式人生 > >Springboot整合JWT做登入攔截

Springboot整合JWT做登入攔截

為什麼需要JWT

在傳統的 session 驗證中,服務端必須儲存 session ID,用於與使用者傳過來的 cookie 驗證。而在一開始儲存 session ID 時, 只會儲存在一臺伺服器上,所以只能由一個 server 應答,就算其他伺服器有空閒也無法應答,因此也利用不到分散式伺服器的優點。
而 JWT 依賴的是在客戶端本地儲存驗證資訊,不需要利用伺服器儲存的資訊來驗證,所以任意一臺伺服器都可以應答,伺服器的資源也被較好地利用。

JWT介紹
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();
    }
}