springboot+jwt實現token登錄權限認證

一 前言

此篇文章的內容也是學習不久,終於到週末有時間碼一篇文章分享知識追尋者的粉絲們,學完本篇文章,讀者將對token類的登錄認證流程有個全面的瞭解,能夠動態搭建本身的登錄認證過程;對小項目而已經是個輕量級的認證機制,符合開發需求;更多精彩原創內容關注公主號知識追尋者,讀者的確定,就是對做者的創做的最大支持java

二 jwt實現登錄認證流程

  • 用戶使用帳號和麪發出post請求
  • 服務器接受到請求後使用私鑰建立一個jwt,這邊會生成token
  • 服務器返回這個jwt給瀏覽器
  • 瀏覽器須要將帶有token的jwt放入請求頭
  • 每次手到客戶端請求,服務器驗證該jwt的token
  • 驗證成功返回響應的資源給瀏覽器。不然異常處理

三 相關介紹jwt

3.1jwt 組成

JWT的token由三段信息構成的,將這三段信息文本用.鏈接一塊兒就構成了JWT字符串;web

  • Header 頭部(包含了令牌的元數據,而且包含簽名和或加密算法的類型)
  • Payload 負載
  • Signature 簽名/簽證
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1ODI4OTc4NDUsInVzZXJuYW1lIjoienN6eHoifQ.vyiExkFWCCmQA3PFYL0jJfIiYGWubngqB0WcgmtHOxg

3.2 jwt優勢

  • 簡潔(Compact): 能夠經過URLPOST參數或者在HTTP header發送,數據量小,傳輸速度快
  • 自包含(Self-contained):負載中包含了全部用戶所須要的信息,避免屢次查詢數據庫
  • .由於Token是以JSON加密的形式保存在客戶端的,因此JWT是跨語言支持;
  • 不須要在服務端保存會話信息,適用於分佈式與微服務;

四 jwt用戶登錄發放token

4.1 pom.xml

項目構件以下算法

  • springboot 2.1;
  • jwt 3.4.0;
  • maven 3.5
  • jdk1.8
  • postman接口測試
<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.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

4.2jwt工具類

jwt工具類中有三個方法,分別是生成數字簽名用於用戶首次登錄時發送jwt給客戶端;其次是校驗方法,用於攔截器攔截全部規則內的url,每一個請求都必須帶有服務器發送的jwt,通過驗證後才放行請求;最後一個得到用戶名的方法用於查詢密鑰,在驗證jwt時做爲參數傳入;spring

/* *
 * @Author lsc
 * <p> JWT工具類 </p>
 * @Param
 * @Return
 */
public class JwtUtil {

    // Token過時時間30分鐘
    public static final long EXPIRE_TIME = 30 * 60 * 1000;

    /* *
     * @Author lsc
     * <p> 校驗token是否正確 </p>
     * @Param token
     * @Param username
     * @Param secret
     * @Return boolean
     */
    public static boolean verify(String token, String username, String secret) {
        try {
            // 設置加密算法
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withClaim("username", username)
                    .build();
            // 效驗TOKEN
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (Exception exception) {
            return false;
        }
    }



    /* *
     * @Author lsc
     * <p>生成簽名,30min後過時 </p>
     * @Param [username, secret]
     * @Return java.lang.String
     */
    public static String sign(String username, String secret) {
        Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
        Algorithm algorithm = Algorithm.HMAC256(secret);
        // 附帶username信息
        return JWT.create()
                .withClaim("username", username)
                .withExpiresAt(date)
                .sign(algorithm);

    }

    /* *
     * @Author lsc
     * <p> 得到用戶名 </p>
     * @Param [request]
     * @Return java.lang.String
     */
    public static String getUserNameByToken(HttpServletRequest request)  {
        String token = request.getHeader("token");
        DecodedJWT jwt = JWT.decode(token);
        return jwt.getClaim("username")
                .asString();
    }



}

4.3 用戶實體

實體中包含用戶名,和密碼,一切從簡;數據庫

/**
 * @Author lsc
 * <p> </p>
 */
@Data
public class SysUser {

    private String username;

    private String password;

}

4.4Controller

表現層代碼用戶用戶登錄認證,認證成功後發放token給客戶端;api

/**
 * @Author lsc
 * <p> </p>
 */
@RestController
public class SysUserController {

    @PostMapping(value = "/login")
    public Map<String, Object> login(@RequestBody SysUser sysUser){
        Map<String, Object> map = new HashMap<>();
        String username = sysUser.getUsername();
        String password = sysUser.getPassword();
        // 省略 帳號密碼驗證
        // 驗證成功後發送token
        String token = JwtUtil.sign(username,password);
        if (token != null){
            map.put("code", "200");
            map.put("message","認證成功");
            map.put("token", token);
            return map;
        }
        map.put("code", "403");
        map.put("message","認證失敗");
        return map;
    }
}

4.5 測試

測試url http://localhost:8080/login跨域

postman post請求測試參數以下瀏覽器

{
    "username": "zszxz",
    "password": "zszxz"
}

返回內容以下springboot

{
    "code": "200",
    "message": "認證成功",
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1ODI4OTc4NDUsInVzZXJuYW1lIjoienN6eHoifQ.vyiExkFWCCmQA3PFYL0jJfIiYGWubngqB0WcgmtHOxg"
}

五 jwt登錄攔截認證

基於前面已經實現jwt登陸認證後發放token給客戶端;本節內容就是將token放入請求頭中發送請求給服務器;服務器使用攔截器攔截請求對token進行驗證;驗證成功請求經過,不然請求資源失敗;服務器

5.1自定義攔截器

自定義攔截器JwtInterceptor,實現HandlerInterceptor接口,每次請求到達以前都會驗證token是否有效;

/**
 * @Author lsc
 * <p>token驗證攔截器 </p>
 */
@Component
public class JwtInterceptor implements HandlerInterceptor {

    @Autowired
    SysUserService sysUserService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 從 http 請求頭中取出 token
        String token = request.getHeader("token");
        // 若是不是映射到方法直接經過
        if(!(handler instanceof HandlerMethod)){
            return true;
        }
        if (token != null){
            String username = JwtUtil.getUserNameByToken(request);
            // 這邊拿到的 用戶名 應該去數據庫查詢得到密碼,簡略,步驟在service直接獲取密碼
            boolean result = JwtUtil.verify(token,username,sysUserService.getPassword());
            if(result){
                System.out.println("經過攔截器");
                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 {

    }
}

5.2 service

/**
 * @Author lsc
 * <p> 模擬查詢數據庫得到帳號密碼 </p>
 */
@Service
public class SysUserService {


    public String getPassword(){
        return "zszxz";
    }
}

5.3攔截器配置

攔截器配置中主要定義攔截請求規則,將攔截器注入WebMvcConfigurer;cors跨域處理;

/* *
 * @Author lsc
 * <p>攔截器配置 </p>
 * @Param
 * @Return
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    /* *
     * @Author lsc
     * <p> 設置攔截路徑 </p>
     * @Param [registry]
     * @Return void
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/login");
    }
    /* *
     * @Author lsc
     * <p> 將攔截器注入context </p>
     * @Param []
     * @Return com.zszxz.jwt.interceptor.JwtInterceptor
     */
    @Bean
    public JwtInterceptor authenticationInterceptor() {
        return new JwtInterceptor();
    }

    /* *
     * @Author lsc
     * <p>跨域支持 </p>
     * @Param [registry]
     * @Return void
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS", "HEAD")
                .maxAge(3600 * 24);
    }
}

5.4Controller

表現層接口用於攔截親求測試

/**
 * @Author lsc
 * <p> </p>
 */
@RestController
public class TestController {

    @GetMapping(value = "/api/test")
    public String get(){

        return "zszxz";
    }
}

5.5 測試

測試url http://localhost:8080/api/test

發送get請求給服務器,帶有請求頭,key爲token,value爲用戶首次登錄時返回的token串;

測試返回內容以下

zszxz

六 官網連接

https://jwt.io/introduction/

源碼 關注公主號或者做者專欄說明便可得到;

相關文章
相關標籤/搜索