Spring Security OAuth2:整合jwt


接着上一篇博客:https://www.cnblogs.com/wwjj4811/p/14505081.html

JWT介紹

JSON Web Token(JWT)是一個開放的行業標准(RFC 7519),它定義了一種緊湊且獨立的方式,用於在各方之間作為JSON對象安全地傳輸信息。此信息可以通過數字簽名進行驗證和信任。JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公鑰/私鑰對進行簽名,防止被篡改

構成

JWT 有三部分構成:頭部、有效載荷、簽名

  • 頭部:包含令牌的類型(JWT)與加密的簽名算法(如 SHA256 或 ES256),Base64編碼后加入第一部分

  • 有效載荷:通俗一點講就是token中需要攜帶的信息都將存於此部分,比如:用戶id、權限標識等信息。

    注:該部分信息任何人都可以讀出來,所以添加的信息需要加密才會保證信息的安全性

  • 簽名:用於防止 JWT 內容被篡改, 會將頭部和有效載荷分別進行 Base64編碼,編碼后用.連接組成新的字符串,然后再使用頭部聲明的簽名算法進行簽名。在具有秘鑰的情況下,可以驗證JWT的准確性,是否被篡改

認證服務器-對稱加密 JWT 令牌

對稱加密就是同一個密鑰可以同時用作信息的加密和解密,這種加密方法稱為對稱加密,也稱為單密鑰加密。

JWT 管理令牌

1.重構 com.wj.oauth2.server.config.TokenConfig 將令牌存儲方式切換為 JWT

@Configuration
public class TokenConfig {

    @Bean
    public TokenStore tokenStore(){
        //jwt管理令牌
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    // JWT 簽名秘鑰
    private static final String SIGNING_KEY = "wj-key";

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey(SIGNING_KEY);
        return jwtAccessTokenConverter;
    }
}

JWT轉換器添加到令牌端點

修改AuthorizationServerConfig類:

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    /**
     * 重寫父類的方法
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //密碼模式需要設置此認證管理器
        endpoints.authenticationManager(authenticationManager);
        // 刷新令牌獲取新令牌時需要
        endpoints.userDetailsService(customUserDetailsService);
        //設置token存儲策略
        endpoints.tokenStore(tokenStore).accessTokenConverter(jwtAccessTokenConverter);
        //授權碼管理策略,針對授權碼模式有效,會將授權碼放到 auth_code 表,授權后就會刪除它
        endpoints.authorizationCodeServices(authorizationCodeServices());
    }

測試

使用密碼授權模式獲取access_token,發現access_token已經響應為jwt令牌

image-20210309142625908

檢查 JWT 令牌:http://localhost:8090/auth/oauth/check_token,已經包含了用戶基本信息

image-20210309143238019

資源服務器-對稱加密 JWT 令牌

在 JwtAccessTokenConverter 中使用了一個對稱密鑰來簽署我們的令牌,意味着我們需要為資源服務器使用同樣的密鑰來驗證簽名合法性。

JWT 管理令牌

這部分與認證服務器令牌配置是一樣的, 將 JWT 令牌部分拷貝到cloud-oauth2-resource-product中的com.wj.oauth2.resource.config 包下即可.

@Configuration
public class TokenConfig {

    @Bean
    public TokenStore tokenStore(){
        //jwt管理令牌
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    // JWT 簽名秘鑰
    private static final String SIGNING_KEY = "wj-key";

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey(SIGNING_KEY);
        return jwtAccessTokenConverter;
    }

}

資源服務器校驗 JWT 令牌

修改ResourceServerConfig配置類:注釋掉tokenService方法,並設置resources的tokenStore,就會自動本地校驗jwt

@Configuration
// 標識為資源服務器, 所有發往當前服務的請求,都會去請求頭里找token,找不到或驗證不通過不允許訪問
@EnableResourceServer
//開啟方法級別權限控制
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    //配置當前資源服務器的ID
    private static final String RESOURCE_ID = "product-server";

    @Autowired
    private TokenStore tokenStore;

    /**當前資源服務器的一些配置, 如資源服務器ID **/
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        // 配置當前資源服務器的ID, 會在認證服務器驗證(客戶端表的resources配置了就可以訪問這個服務)
        resources.resourceId(RESOURCE_ID)
            // 實現令牌服務, ResourceServerTokenServices實例
            .tokenStore(tokenStore);
    }

/*    @Bean
    public ResourceServerTokenServices tokenService() {
        // 資源服務器去遠程認證服務器驗證 token 是否有效
        RemoteTokenServices service = new RemoteTokenServices();
        // 請求認證服務器驗證URL,注意:默認這個端點是拒絕訪問的,要設置認證后可訪問
        service.setCheckTokenEndpointUrl("http://localhost:8090/auth/oauth/check_token");
        // 在認證服務器配置的客戶端id
        service.setClientId("wj-pc");
        // 在認證服務器配置的客戶端密碼
        service.setClientSecret("wj-secret");
        return service;
    }*/


    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.sessionManagement()
                //不創建session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                //資源授權規則
                .authorizeRequests()
                .antMatchers("/product/**").hasAuthority("product")
                //所有的請求對應訪問的用戶都要有all范圍的權限
                .antMatchers("/**").access("#oauth2.hasScope('all')");
    }
}

測試

密碼認證模式先獲取access_token,再通過 JWT令牌查詢獲取商品資源

image-20210309145538851

為了安全性, JWT令牌每次請求獲取令牌都是響應一個新令牌,因為里面已經包含用戶信息, 而之前的是令牌在有效時間里每次請求都是響應一樣的令牌。

認證服務器-非對稱加密 JWT 令牌

非對稱加密算法需要兩個密鑰:公開密鑰(publickey:簡稱公鑰)和私有密鑰(privatekey:簡稱私鑰)。公鑰與私鑰是一對,如果用私鑰對數據進行加密,只有用對應的公鑰才能解密。

生成密鑰證書

公私密鑰可以用jdk的命令keytool生成:別名為 oauth2,秘鑰算法為 RSA,秘鑰口令為 oauth2,秘鑰庫(文件)名稱為 oauth2.jks,秘鑰庫(文件)口令為 oauth2

keytool -genkeypair -alias oauth2 -keyalg RSA -keypass oauth2 -keystore oauth2.jks -storepass oauth2

image-20210309150042457

生成后,在命令執行命令的所在目錄下會有一個oauth2.jks文件

image-20210309150157623

將該文件移動到認證服務的resources目錄下:

image-20210309150301481

非對稱加密 JWT 令牌

修改認證服務的TokenConfig類:

@Configuration
public class TokenConfig {

    @Bean
    public TokenStore tokenStore(){
        //return new JdbcTokenStore(dataSource);
        //jwt管理令牌
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        KeyStoreKeyFactory keyFactory = new KeyStoreKeyFactory(new ClassPathResource("oauth2.jks"), "oauth2".toCharArray());
        jwtAccessTokenConverter.setKeyPair(keyFactory.getKeyPair("oauth2"));
        return jwtAccessTokenConverter;
    }

}

pom文件

修改認證服務的pom.xml

    <build>
        <resources>
            <resource>
                <!-- 防止JKS被maven錯誤解析 -->
                <directory>src/main/resources</directory>
                <filtering>false</filtering>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.2.4.RELEASE</version>
                <configuration>
                    <mainClass>com.wj.oauth2.AuthServerApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

因為有可能jks文件無法正確被maven解析,導致項目啟動報錯。

資源服務器-非對稱加密 JWT 令牌

根據密鑰文件獲取公鑰

OpenSSL 是一個加解密工具包,可以使用 OpenSSL 來獲取公鑰,下載網址:http://slproweb.com/products/Win32OpenSSL.html

安裝完成后,配置openssl環境變量,即安裝目錄\bin

image-20210309151209245

獲取公鑰

進入oauth2.jks所在目錄執行命令:

keytool -list -rfc --keystore oauth2.jks | openssl x509 -inform pem -pubkey

image-20210309151410165

復制出控制台打印出的內容到public.txt,並放到資源服務器的resources目錄下(需要復制出public key的內容)

image-20210309152457825

非對稱加密 JWT 令牌

修改資源服務器的TokenConfig類:

@Slf4j
@Configuration
public class TokenConfig {

    @Bean
    public TokenStore tokenStore(){
        //jwt管理令牌
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    // JWT 簽名秘鑰
    private static final String SIGNING_KEY = "wj-key";

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        ClassPathResource classPathResource = new ClassPathResource("public.txt");
        String publicKey = null;
        try {
            publicKey=IOUtils.toString(classPathResource.getInputStream(),"UTF-8");
            log.info("publicKey:{}" , publicKey);
        } catch (IOException e) {
            e.printStackTrace();
        }
        jwtAccessTokenConverter.setVerifierKey(publicKey);
        return jwtAccessTokenConverter;
    }
}

測試

先請求access_token令牌:發現比對稱加密的字符數目多了不少

image-20210309153325913

發送請求,仍然可以請求成功

image-20210309153418251


免責聲明!

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



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