溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何用代碼實現(xiàn)Jwt 登錄認證

發(fā)布時間:2021-11-24 15:38:43 來源:億速云 閱讀:186 作者:柒染 欄目:大數(shù)據(jù)

如何用代碼實現(xiàn)Jwt 登錄認證,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

基于 nimbus-jose-jwt 封裝好了一個 jwt,并且只需要自定義 HandlerInterceptor 實現(xiàn)類和 WebMvcConfigurer 實現(xiàn)類就可以在項目中引入使用 jwt 做登錄認證。

Spring Boot 中的 starter 是一種非常重要的機制,能夠拋棄以前繁雜的配置,將其統(tǒng)一集成進 starter,應(yīng)用只需要在 Maven 中引入 starter 依賴,Spring Boot 就能自動掃描到要加載的信息并啟動相應(yīng)的默認配置。starter 讓我們擺脫了各種依賴庫的處理,需要配置各種信息的困擾。Spring Boot 會自動通過 classpath 路徑下的類發(fā)現(xiàn)需要的 Bean,并注冊進 IOC 容器。Spring Boot 提供了針對日常企業(yè)應(yīng)用研發(fā)各種場景的 spring-boot-starter 依賴模塊。所有這些依賴模塊都遵循著約定成俗的默認配置,并允許我們調(diào)整這些配置,即遵循“約定大于配置”的理念。

接下來,我們就在之前封裝的 ron-jwt 基礎(chǔ)上自定義的一個 starter,項目只要在依賴中引入這個 starter,再定義簡單的配置就可以使用 jwt 的實現(xiàn)登錄認證。

新建工程并配置依賴

Spring Boot 提供的 starter 以 spring-boot-starter-xxx 的方式命名的。官方建議自定義的 starter 使用 xxx-spring-boot-starter 命名規(guī)則,以區(qū)分 Spring Boot 生態(tài)提供的 starter。所以我們新建工程 ron-jwt-spring-boot-starter。

如何用代碼實現(xiàn)Jwt 登錄認證

在 pom.xml 中引入依賴

<dependency>
  <groupid>org.springframework.boot</groupid>
  <artifactid>spring-boot-configuration-processor</artifactid>
  <optional>true</optional>
</dependency>
<dependency>
  <groupid>org.springframework.boot</groupid>
  <artifactid>spring-boot-autoconfigure</artifactid>
</dependency>
<dependency>
  <groupid>org.springframework.boot</groupid>
  <artifactid>spring-boot-starter-web</artifactid>
</dependency>
<dependency>
  <groupid>io.ron</groupid>
  <artifactid>ron-jwt</artifactid>
  <version>1.0-SNAPSHOT</version>
</dependency>

spring-boot-configuration-processor 主要的作用是在編譯時在 META-INF 下生成 spring-configuration-metadata.json 文件,該文件主要為IDE 使用,即可以通過在 application.properties 文件中通過 ctrl + 點擊進入配置屬性所在的類中。

spring-boot-autoconfigure 主要作用是提供自動裝配功能。

spring-boot-starter-web 則是因為我們將會內(nèi)置 HandlerInterceptor 實現(xiàn)類和 WebMvcConfigurer 實現(xiàn)類。

ron-jwt 是我們在上一篇中封裝好的 jwt 庫。

定義配置項管理類

我們定義 JwtProperties 來聲明 starter 的使用者可使用哪些配置項。

@ConfigurationProperties(prefix = "ron.jwt")
public class JwtProperties {

    private String tokenName = JwtUtils.DEFAULT_TOKEN_NAME;

    private String hmacKey;

    private String jksFileName;

    private String jksPassword;

    private String certPassword;

    // 簽發(fā)人
    private String issuer;

    // 主題
    private String subject;

    // 受眾
    private String audience;

    private long notBeforeIn;

    private long notBeforeAt;

    private long expiredIn;

    private long expiredAt;
}

@ConfigurationProperties 注解指定了所有配置項的前綴為 ron.jwt。

@ConfigurationProperties 的基本用法非常簡單:我們?yōu)槊總€要捕獲的外部屬性提供一個帶有字段的類。請注意以下幾點:

  • 前綴定義了哪些外部屬性將綁定到類的字段上。

  • 根據(jù) Spring Boot 寬松的綁定規(guī)則,類的屬性名稱必須與外部屬性的名稱匹配。

  • 我們可以簡單地用一個值初始化一個字段來定義一個默認值。

  • 類本身可以是包私有的。

  • 類的字段必須有公共 setter 方法。

> Spring Boot 寬松的綁定規(guī)則(relaxed binding): > > Spring Boot 使用一些寬松的綁定屬性規(guī)則。因此,以下變體都將綁定到 tokenName 屬性上: > > + ron.jwt.tokenname=Authorization > + ron.jwt.tokenName=Authorization > + ron.jwt.token_name=Authorization > + ron.jwt.token-name=Authorization

實現(xiàn)相關(guān)的功能

在上一篇中,我們把 HandlerInterceptor 實現(xiàn)類和 WebMvcConfigurer 實現(xiàn)類放在具體的業(yè)務(wù)項目中自己實現(xiàn)。實際上這部分也是項目中比較通用的邏輯,因此我們考慮將這些實現(xiàn)放置在 starter 中。項目中可以不做多余的自定義,直接通過引入 starter 就可以使用 jwt 認證的功能。

JwtInterceptor
public class JwtInterceptor implements HandlerInterceptor {

    private Logger logger = LoggerFactory.getLogger(JwtInterceptor.class);

    private static final String PREFIX_BEARER = "Bearer ";

    @Autowired
    private JwtProperties jwtProperties;

    @Autowired
    private JwtService jwtService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {

        // 如果不是映射到方法直接通過
        if(!(handler instanceof HandlerMethod)){
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        // 檢查是否有 @AuthRequired 注解,有且 required() 為 false 則跳過
        if (method.isAnnotationPresent(AuthRequired.class)) {
            AuthRequired authRequired = method.getAnnotation(AuthRequired.class);
            if (!authRequired.required()) {
                return true;
            }
        }

        String token = request.getHeader(jwtProperties.getTokenName());

        logger.info("token: {}", token);

        if (StringUtils.isEmpty(token) || token.trim().equals(PREFIX_BEARER.trim())) {
            return true;
        }

        token = token.replace(PREFIX_BEARER, "");

        // 設(shè)置線程局部變量中的 token
        JwtContext.setToken(token);

        // 在線程局部變量中設(shè)置真實傳遞的數(shù)據(jù),如當前用戶信息等
        String payload = jwtService.verify(token);
        JwtContext.setPayload(payload);

        return onPreHandleEnd(request, response, handler, payload);
    }

    public boolean onPreHandleEnd(HttpServletRequest request, HttpServletResponse response,
                                  Object handler, String payload) throws Exception {
        return true;
    }

    @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 {
        // 務(wù)必在線程結(jié)束前清理線程局部變量
        JwtContext.removeAll();
    }
}

onPreHandleEnd 方法是一個默認實現(xiàn),如業(yè)務(wù)上有必要,也可以繼承 JwtInterceptor,在這個方法中添加自定義的邏輯。一個可能的場景是將 JWT 的 token 放到 Redis 中進行超時管理。

JwtInterceptorConfig
public class JwtInterceptorConfig implements WebMvcConfigurer {

    private JwtInterceptor jwtInterceptor;

    public JwtInterceptorConfig(JwtInterceptor jwtInterceptor) {
        this.jwtInterceptor = jwtInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor).addPathPatterns("/**");
    }
}

這里默認攔截了所有的請求,在上一篇文章中,我們提到可以配合 @AuthRequired 來過濾不需要攔截的請求。

編寫自動配置邏輯

JwtAutoConfiguration
@Configuration
@EnableConfigurationProperties(JwtProperties.class)
public class JwtAutoConfiguration {

    @Autowired
    private JwtProperties jwtProperties;

    @Bean
    public JwtConfig jwtConfig() {
        JwtConfig jwtConfig = new JwtConfig();
        BeanUtils.copyProperties(jwtProperties, jwtConfig);
        return jwtConfig;
    }

    @Bean
    public JwtService jwtService() {
        JwtConfig jwtConfig = jwtConfig();
        return JwtUtils.obtainJwtService(jwtConfig);
    }

    @Bean
    public JwtInterceptor jwtInterceptor() {
        return new JwtInterceptor();
    }

    @Bean
    public JwtInterceptorConfig jwtInterceptorConfig() {
        return new JwtInterceptorConfig(jwtInterceptor());
    }
}

@EnableConfigurationProperties 的作用是引入使用 @ConfigurationProperties 注解的類,并使其生效。

@EnableConfigurationProperties 文檔中解釋:當 @EnableConfigurationProperties 注解應(yīng)用到你的@Configuration 時, 任何被 @ConfigurationProperties 注解的 beans 將自動被 Environment 屬性配置。 這種風格的配置特別適合與 SpringApplication 的外部 YAML 配置進行配合使用。

集成 starter 使之生效

有兩種方式可以讓 starter 在應(yīng)用中生效。

通過 SPI 機制加載 - 被動生效

通過 Spring Boot 的 SPI 的機制來去加載我們的starter。

在 resources 目錄下新建 WEB-INF/spring.factories 文件。

META-INF/spring.factories 文件是 Spring Boot 框架識別并解析 starter 的核心文件。spring.factories 文件是幫助 Spring Boot 項目包以外的 Bean(即在 pom 文件中添加依賴中的 Bean)注冊到 Spring Boot 項目的 Spring 容器。由于 @ComponentScan 注解只能掃描Spring Boot 項目包內(nèi)的 Bean 并注冊到 Spring 容器中,因此需要 @EnableAutoConfiguration 注解來注冊項目包外的 Bean。而 spring.factories 文件,則是用來記錄項目包外需要注冊的 Bean 類名。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.ron.jwt.starter.JwtAutoConfiguration
自定義 Enable 注解引入 - 主動生效

在 starter 組件集成到我們的 Spring Boot 應(yīng)用時需要主動聲明啟用該 starter 才生效,我們通過自定義一個 @Enable 注解然后在把自動配置類通過 Import 注解引入進來。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({JwtAutoConfiguration.class})
@Documented
@Inherited
public @interface EnableJwt {

}

如果使用主動生效的方式,那么實現(xiàn)被動生效的 META-INF/spring.factories 文件需要移除。

打包并發(fā)布 starter

使用 mvn install 可以打包并安裝在本地;

使用 mvn deploy 可以發(fā)布到遠程倉庫。


測試應(yīng)用程序

在 pom.xml 中修改依賴,引入 ron-jwt-spring-boot-starter。

<dependency>
  <groupid>io.ron</groupid>
  <artifactid>ron-jwt-spring-boot-starter</artifactid>
  <version>1.0-SNAPSHOT</version>
</dependency>

去掉 HandlerInterceptor 實現(xiàn)類和 WebMvcConfigurer 實現(xiàn)類。

在 application.yml 中配置 ron.jwt.hmac-key 的值,就可以提供 HMAC 算法實現(xiàn)的 JWT 簽名與驗證功能了。

如果 starter 采用的被動生效方式,現(xiàn)在就可以運行程序,然后使用 Postman 進行測試并觀察結(jié)果。

如果 starter 采用的主動生效方式,需要在項目啟動類上添加 @EnableJwt 注解將 jwt-starter 引入。

@SpringBootApplication
public class JwtStarterApplication {

    public static void main(String[] args) {
        SpringApplication.run(JwtStarterApplication.class, args);
    }
}

看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進一步的了解或閱讀更多相關(guān)文章,請關(guān)注億速云行業(yè)資訊頻道,感謝您對億速云的支持。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

jwt
AI