spring boot+jwt 權限驗證


       上周看了一下jwt以前公司的開發都是使用session共享的方法。現在主流的兩種方式一種是把登錄信息保存在服務器,另一種則是把信息保存在客戶端。在使用session 存儲的時候會遇到很多的問題,隨着項目越來越多工作量會變得越來越大。現在公司要開始一個新的項目,索性就開始使用jwt,數據保存在客戶端每一次請求都回傳給服務器驗證一下。

      本文分為兩部分第一部分簡單介紹一下JWT,第二部分簡單介紹一下使用spring boot+jwt構建一個項目。

一、什么是JWT?

     JWT全程JSON Web tokens 主要由 三部分組成通過“.”分割開,三部分分別是Header、Payload、Signature因此一個完成的JWT經典結構體應該是xxxx.yyyy.zzzzz

1.Header (頭)

       頭部是一個包括了兩部分的JSON 一部分是簽名的算法(alg)通常使用HS256或者RSA,這里基本上都是使用HS256,還有一個部分是簽名類型(typ)即JWT

例如:

{
  "alg": "HS256",
  "typ": "JWT"
}

        然后把這個JSON Base64Url,形成第一部分。

2.Payload (數據)

        第二部分也是一個JSON對像,官方提供了幾個數據字段 iss (issuer):簽發人、exp (expiration time):過期時間、sub (subject):主題、aud (audience):受眾、nbf (Not Before):生效時間、iat (Issued At):簽發時間、jti (JWT ID):編號

除了官方字段還可以定義私有字段

如例:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

  然后和第一部分一樣使用Base64Url算法轉化一下,由於這部分直接是暴露出去的顧不應該放比較重要的數據。

3.Signature

      Signature 部分是對前兩部分的簽名,防止數據篡改。

      首先,需要指定一個密鑰(secret)。這個密鑰只有服務器才知道,不能泄露給用戶。然后,使用 Header 里面指定的簽名算法(默認是 HMAC SHA256),按照下面的公式產生簽名。

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

      算出簽名以后,把 Header、Payload、Signature 三個部分拼成一個字符串,每個部分之間用"點"(.)分隔,就可以返回給用戶。

      關於jwt的介紹就到這里 ,參考了網上的其他人博客和JWT官網想要了解的更加詳細可以前去官網仔細閱讀一下。

二、構建demo

      首先新建一個spring boot 項目 pom文件如下

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.7.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

  然后新建一個實體類user 包括了id,name,password和一個EntRespon用於網絡之間消息的傳輸。

public class User {
    private String id;
    private String name;
    private String password;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

  

 

public class EntRespson {
    private int code;
    private String resultMsg;
    private Object data;

    public EntRespson() {
        this.code=0;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getResultMsg() {
        return resultMsg;
    }

    public void setResultMsg(String resultMsg) {
        this.resultMsg = resultMsg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

   然后新建一個controller 先寫一個測試方法 test 先測試沒有問題 。然后我們需要完成一個JWT加密解密的方法,以下使我們的一個簡單的JWT加密解密的方法。

public class JwtUtils {

    public static final String TOKEN_HEADER = "Authorization";
    public static final String TOKEN_PREFIX = "Bearer ";

    private static final String SECRET = "jwtdemo";
    private static final String ISS = "echisan";

    // 過期時間
    private static final long EXPIRATION = 360000l;
    


    /**
     * 加密 jwt token
     * @param id
     * @return
     */
    public static String encode(String id) {
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        String token = JWT.create()
                //設置過期時間為一個小時
                .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION))
                //設置負載
                .withJWTId(id)
                .sign(algorithm);
        return token;
    }

    /**
     *  解密 jwt toke
     * @param token
     * @return
     */
    public static String decode(String token) {
        if (token == null || token.length() == 0) {
            throw new RuntimeException("token為空:" + token);
        }
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        DecodedJWT decodedJWT = jwtVerifier.verify(token);
        return decodedJWT.getId();
    }

}

     由於我們這demo沒有連接數據庫所以我把用戶信息寫在配置文件里面了 application.properties 如下

demo.name=lee
demo.password=123
demo.id=1

  然后我們完成一個讀取配置文件的類

@Configuration
@PropertySource("classpath:application.properties")
public class UserConfig {
    @Value("${demo.name}")
    private String name;
    @Value("${demo.password}")
    private String password;
    @Value("${demo.id}")
    private String id;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

  

         這個時候完成controller 里面的登錄方法,在登錄完成后 生成一個token返還給前端這個就是一個獲取受保護的憑證。

@RestController
public class CtrlUser {
    @Autowired
    private UserConfig config;

    @RequestMapping("test1")
    public String test() {
        return "ok test" + config.getName();
    }

    @RequestMapping(value = "login", method = RequestMethod.POST)
    public EntRespson login(@RequestBody User user) {
        EntRespson entRespson = new EntRespson();
        try {
            String usename = config.getName();
            String pwds = config.getPassword();
            if (!(user.getName().equals(usename) &&
                    user.getPassword().equals(pwds)))throw new Exception("賬戶密碼錯誤");
            //登錄成功獲得token
            String token = JwtUtils.encode(config.getId());
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("token",token);
            entRespson.setData(jsonObject);
        } catch (Exception e) {
            entRespson.setCode(-1);
            entRespson.setResultMsg(e.getMessage());
        }
        return entRespson;
    }
}

     現在我們還是完成一個簡單的攔截器的功能。

public class Interceptor implements HandlerInterceptor {
    @Autowired
    private UserConfig config;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");// HttpServletRequest 請求頭中取出 token
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

//        HandlerMethod handlerMethod = (HandlerMethod) handler;  
//        Method method = handlerMethod.getMethod();
//        //檢查是否有passtoken注釋,有則跳過認證
//        if (method.isAnnotationPresent(SkipToken.class)) {
//            SkipToken passToken = method.getAnnotation(SkipToken.class);
//            if (passToken.required()) {
//                return true;
//            }
//        }
        String id = JwtUtils.decode(token);
        if (id.equals(config.getId()))
            return true;
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

  把攔截器添加到配置類中

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(getInterceptor())
                .addPathPatterns("/**")             //配置攔截所有請求
                .excludePathPatterns("/login");    // 不攔截登錄請求。
    }

    @Bean
    public Interceptor getInterceptor(){
        return new Interceptor();
    }
}

  好了 到此為止我們的demo已經完成了我們開始測試我們是demo。

       啟動完成之后我們先試試127.0.0.1:8080/test1

   這里可以看到我們請求被拒絕了 ,然后我們測試一下登錄接口。

       到這里我們已經獲取到了token,前端在獲取了token之后前端是邏輯是需要在每一次請求的Headers里面增加一個token,我們在上一個測試接口里面加上token重新請求一下

      好的在這里我們已經能看到了我們帶有token的請求已經訪問成功了。

     最后附上源碼地址:https://github.com/llcin/spring_tool/tree/master/mydemo

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM