1. 程式人生 > 實用技巧 >專案總結67:Springboot中使用JWT+Token鑑權示例

專案總結67:Springboot中使用JWT+Token鑑權示例

專案總結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