中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

如何解決使用JWT作為Spring?Security?OAuth2的token存儲問題

發布時間:2021-12-24 11:35:12 來源:億速云 閱讀:387 作者:小新 欄目:開發技術

小編給大家分享一下如何解決使用JWT作為Spring Security OAuth2的token存儲問題,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!

Spring Security OAuth3的demo在前幾篇文章中已經講過了,在那些模式中使用的都是RemoteTokenService調用授權服務器來校驗token,返回校驗通過的用戶信息供上下文中獲取

這種方式會加重授權服務器的負載,你想啊,當用戶沒授權時候獲取token得找授權服務器,有token了訪問資源服務器還要訪問授權服務器,相當于說每次請求都要訪問授權服務器,這樣對授權服務器的負載會很大

常規的方式有兩種來解決這個問題:

  • 使用JWT作為Token傳遞

  • 使用Redis存儲Token,資源服務器本地訪問Redis校驗Token

使用JWT與Redis都可以在資源服務器中進行校驗Token,從而減少授權服務器的工作量

JWT默認使用HMACSHA256對稱加密算法,以下記錄下默認算法實現與非對稱RSA算法的集成,使用不同算法加解密測試方法是一致的,所以放在文章最后

授權服務器整合JWT——對稱加解密算法

授權服務器整體代碼結構

如何解決使用JWT作為Spring?Security?OAuth2的token存儲問題

pom.xml中引入依賴

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>        
		<!-- Spring Security OAuth3 -->
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth3</artifactId>
            <version>2.4.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
            <version>1.1.0.RELEASE</version>
        </dependency>

SecurityConfig配置,主要需要顯式聲明AuthenticationManager和UserDetailsService這兩個bean

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Bean
    public UserDetailsService userDetailsService(){ //主要是配置這個Bean,用于授權服務器配置中注入
        return super.userDetailsService();
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // @formatter: off
        auth.inMemoryAuthentication()
                .withUser("hellxz")
                .password(passwordEncoder().encode("xyz"))
                .authorities(Collections.emptyList());
        // @formatter: on
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated() //所有請求都需要通過認證
                .and()
                .httpBasic() //Basic提交
                .and()
                .csrf().disable(); //關跨域保護
    }
}

授權服務器配置AuthorizationConfig

@Configuration
@EnableAuthorizationServer //開啟授權服務
public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    public UserDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //允許表單提交
        security.allowFormAuthenticationForClients()
                .checkTokenAccess("permitAll()")
                .tokenKeyAccess("permitAll()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // @formatter: off
        clients.inMemory()
                .withClient("client-a") //client端唯一標識
                    .secret(passwordEncoder.encode("client-a-secret")) //client-a的密碼,這里的密碼應該是加密后的
                    .authorizedGrantTypes("authorization_code", "password", "refresh_token") //授權模式標識,這里主要測試用password模式,另外refresh_token不是一種模式,但是可以使用它來刷新access_token(在它的有效期內)
                    .scopes("read_user_info") //作用域
                    .resourceIds("resource1") //資源id,如不需限制資源id,注釋此處即可
                    .redirectUris("http://localhost:9001/callback"); //回調地址

        // @formatter: on
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                .tokenStore(jwtTokenStore()) //設置jwtToken為tokenStore
                .accessTokenConverter(jwtAccessTokenConverter());//設置access_token轉換器
    }

    /**
     * jwt訪問token轉換器
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("my-sign-key"); //資源服務器需要配置此選項方能解密jwt的token
        return converter;
    }

    /**
     * jwt的token存儲對象
     */
    @Bean
    public JwtTokenStore jwtTokenStore(){
        return new JwtTokenStore(jwtAccessTokenConverter());
    }
}

這里主要是在configure(AuthorizationServerEndpointsConfigurer endpoints)授權服務的端點配置中加入JWT的tokenStore和access_token的轉換器,以及這二者的聲明Bean方法

這里使用的是默認對稱MAC算法,即加密解密使用相同的密鑰

啟動類就不說了,開啟@SpringBootApplicatin的main方法

資源服務器整合JWT——對稱加解密算法

資源服務器主要就一個資源配置類

@Configuration
@EnableResourceServer
public class ResourceConfig extends ResourceServerConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        //設置創建session策略
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        //@formatter:off
        //所有請求必須授權
        http.authorizeRequests()
                .anyRequest().authenticated();
        //@formatter:on
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
    	//@formatter:off
    	//如不需要限制資源id,請在授權配置處去除resourceIds的配置
    	resources.resourceId("resource1")
        		 .tokenStore(jwtTokenStore());
    	//@formatter:on
    }

    /**
     * jwt訪問token轉換器
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("my-sign-key"); //與授權服務器相同的signingKey
        return converter;
    }

    /**
     * jwt的token存儲對象
     */
    @Bean
    public JwtTokenStore jwtTokenStore(){
        return new JwtTokenStore(jwtAccessTokenConverter());
    }
}

配置JWT的TokenStore和AccessTokenConverter與授權服器相同,添加啟動類完成配置

OAuth整合JWT——非對稱加解密RSA

本部分基于對稱加密部分,僅展示需要修改的部分

首先使用keytool生成jks (Java Key Store) 密鑰,按提示輸入姓氏等信息

keytool -genkeypair -alias hellxz-jwt -validity 3650 -keyalg RSA -keypass hellxzTest -keystore hellxz-jwt.jks -storepass hellxzTest

生成的私鑰文件會在當前目錄,把hellxz-jwt.jks復制到授權服務器的resources目錄下
授權服務器需修改jwtAccessTokenConverter()

 @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        KeyStoreKeyFactory storeKeyFactory = new KeyStoreKeyFactory(
                new ClassPathResource("hellxz-jwt.jks"), "hellxzTest".toCharArray());
        converter.setKeyPair(storeKeyFactory.getKeyPair("hellxz-jwt"));
        return converter;
    }

在hellxz-jwt.jks同目錄下,執行命令生成公鑰

? keytool -list -rfc --keystore hellxz-jwt.jks | openssl x509 -inform pem -pubkey

輸入密鑰庫口令:  hellxzTest

-----BEGIN PUBLIC KEY-----

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxU7zulFUVBXmZD28xwM4

ul5e9yFrToLgWKHlNLlp904/GbiWBoZ4tcBcNq3VxLGBN9VOqfP1P5C7fRgz95UI

7ShKCKgsFFGL2rAqsplMDClN/adfsxmpF06rVIkGgce9tR0Q0iONcaN+b/lArK4T

Au76QsQwn9MLXlznVfczclZOZSfDNju+1JuBzqt6fEPWqalBUVYdV0zCUDG8ikN1

l9D0m1tSSaKpiTrU2yEUGUji+79Ury7Y8BClEX6d4CTl9TQAhL5g32GoJEc0S2y+

0bqeqUsv1nUt9KiJT9kiOvA+Q7o2T8OHuqQT9le7kvmIi4gSX5vSNvvZagE2Uglh

zQIDAQAB

-----END PUBLIC KEY-----

-----BEGIN CERTIFICATE-----

MIIDUTCCAjmgAwIBAgIEePeDczANBgkqhkiG9w0BAQsFADBZMQswCQYDVQQGEwJD

TjEQMA4GA1UECBMHYmVpamluZzEQMA4GA1UEBxMHYmVpamluZzEKMAgGA1UEChMB

MDEKMAgGA1UECxMBMDEOMAwGA1UEAxMFemhhbmcwHhcNMTkxMjE1MDUyOTM2WhcN

MjkxMjEyMDUyOTM2WjBZMQswCQYDVQQGEwJDTjEQMA4GA1UECBMHYmVpamluZzEQ

MA4GA1UEBxMHYmVpamluZzEKMAgGA1UEChMBMDEKMAgGA1UECxMBMDEOMAwGA1UE

AxMFemhhbmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFTvO6UVRU

FeZkPbzHAzi6Xl73IWtOguBYoeU0uWn3Tj8ZuJYGhni1wFw2rdXEsYE31U6p8/U/

kLt9GDP3lQjtKEoIqCwUUYvasCqymUwMKU39p1+zGakXTqtUiQaBx721HRDSI41x

o35v+UCsrhMC7vpCxDCf0wteXOdV9zNyVk5lJ8M2O77Um4HOq3p8Q9apqUFRVh2X

TMJQMbyKQ3WX0PSbW1JJoqmJOtTbIRQZSOL7v1SvLtjwEKURfp3gJOX1NACEvmDf

YagkRzRLbL7Rup6pSy/WdS30qIlP2SI68D5DujZPw4e6pBP2V7uS+YiLiBJfm9I2

+9lqATZSCWHNAgMBAAGjITAfMB0GA1UdDgQWBBQF96rK7n0XufnvtJuH9tD9Ixza

6zANBgkqhkiG9w0BAQsFAAOCAQEAuMzWZJhej6+4TGgodQKQ5L5RBtOUbesxA1Ue

s9iA4m/jNZnVCXJE0nY47YVzBCIkIsYALswGooMj1PIJxEMpggXVmIuiJpaPgg+4

sthzISxKzX0ru8IrJTapaglMi74ai6S73LTBSke9GEPgWWnbtdUZoUSiSNt1oJ0J

EhFHdPuzxc36neDFRBOBxW4w3qhsTlKTN2wJm1nLV96nFKmqJhQJhhKt6ihe7hMg

qWxzNsWAqv9gJNdKZt5teqwNKT6H7r1NX5oJkJ0Kn1dZy0O3rDDd5E0KDKkMtwOh

3deJH6Uvtt/dw/drzJlByNDEPp6hYGQu2dW5JG5uiHuzFHnJeA==

-----END CERTIFICATE-----

復制公鑰部分到public.cert放到資源服務器的resources目錄

-----BEGIN PUBLIC KEY-----

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxU7zulFUVBXmZD28xwM4

ul5e9yFrToLgWKHlNLlp904/GbiWBoZ4tcBcNq3VxLGBN9VOqfP1P5C7fRgz95UI

7ShKCKgsFFGL2rAqsplMDClN/adfsxmpF06rVIkGgce9tR0Q0iONcaN+b/lArK4T

Au76QsQwn9MLXlznVfczclZOZSfDNju+1JuBzqt6fEPWqalBUVYdV0zCUDG8ikN1

l9D0m1tSSaKpiTrU2yEUGUji+79Ury7Y8BClEX6d4CTl9TQAhL5g32GoJEc0S2y+

0bqeqUsv1nUt9KiJT9kiOvA+Q7o2T8OHuqQT9le7kvmIi4gSX5vSNvvZagE2Uglh

zQIDAQAB

-----END PUBLIC KEY-----

修改資源服務器jwtAccessTokenConverter()方法

  @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        Resource resource = new ClassPathResource("public.cert");
        String publicKey;
        try {
            publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        converter.setVerifierKey(publicKey);
        return converter;
    }

測試驗證

發送POST請求http://localhost:8080/oauth/token?username=hellxz&password=xyz&scope=read_user_info&grant_type=password

如何解決使用JWT作為Spring?Security?OAuth2的token存儲問題

返回結果

如何解決使用JWT作為Spring?Security?OAuth2的token存儲問題

帶token訪問資源服務器

如何解決使用JWT作為Spring?Security?OAuth2的token存儲問題

測試通過

另外使用JWT應設置盡量短的過期時間,因為JWT的token無法手動revoke,只能等待其到達過期時間失效

看完了這篇文章,相信你對“如何解決使用JWT作為Spring Security OAuth2的token存儲問題”有了一定的了解,如果想了解更多相關知識,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

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

AI

曲沃县| 海丰县| 交口县| 蕉岭县| 抚顺市| 奉节县| 连江县| 武夷山市| 兴城市| 桦甸市| 大悟县| 阿瓦提县| 汝南县| 台东县| 齐河县| 华蓥市| 吴堡县| 安徽省| 池州市| 桃源县| 禄丰县| 平乡县| 东乌珠穆沁旗| 虹口区| 应用必备| 唐山市| 光泽县| 稻城县| 昌宁县| 策勒县| 基隆市| 密云县| 监利县| 安平县| 南雄市| 方正县| 尼玛县| 额济纳旗| 慈利县| 达孜县| 黔东|