spring-security-oauth2 中優雅的擴展自定義(短信驗證碼)登錄方式-系列1


1、概括

在博客中,我們將討論如何讓Spring Security OAuth2實現使用JSON Web Tokens。

2、Maven 配置

首先,我們需要在我們的pom.xml中添加spring-security-jwt依賴項。

<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> </dependency>

 

我們需要為Authorization Server和Resource Server添加spring-security-jwt依賴項。

3、授權服務器

接下來,我們將配置我們的授權服務器使用JwtTokenStore - 如下所示

@Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(tokenStore()) .accessTokenConverter(accessTokenConverter()) .authenticationManager(authenticationManager); } @Bean public TokenStore tokenStore() { return new JwtTokenStore(accessTokenConverter()); } @Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey("123"); return converter; } @Bean @Primary public DefaultTokenServices tokenServices() { DefaultTokenServices defaultTokenServices = new DefaultTokenServices(); defaultTokenServices.setTokenStore(tokenStore()); defaultTokenServices.setSupportRefreshToken(true); return defaultTokenServices; } }

請注意,在JwtAccessTokenConverter中使用了一個對稱密鑰來簽署我們的令牌 - 這意味着我們需要為資源服務器使用同樣的確切密鑰。

4、資源服務器

現在,我們來看看我們的資源服務器配置 - 這與授權服務器的配置非常相似:

@Configuration @EnableResourceServer public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(ResourceServerSecurityConfigurer config) { config.tokenServices(tokenServices()); } @Bean public TokenStore tokenStore() { return new JwtTokenStore(accessTokenConverter()); } @Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey("123"); return converter; } @Bean @Primary public DefaultTokenServices tokenServices() { DefaultTokenServices defaultTokenServices = new DefaultTokenServices(); defaultTokenServices.setTokenStore(tokenStore()); return defaultTokenServices; } }

請記住,我們將這兩個服務器定義為完全獨立且可獨立部署的服務器。這就是我們需要在新配置中再次聲明一些相同的bean的原因。

5、令牌中的自定義聲明

現在讓我們設置一些基礎設施,以便能夠在訪問令牌中添加一些自定義聲明。框架提供的標准聲明都很好,但大多數情況下我們需要在令牌中使用一些額外的信息來在客戶端使用。 我們將定義一個TokenEnhancer來定制我們的Access Token與這些額外的聲明。 在下面的例子中,我們將添加一個額外的字段“組織”到我們的訪問令牌 - 與此CustomTokenEnhancer:

public class CustomTokenEnhancer implements TokenEnhancer { @Override public OAuth2AccessToken enhance( OAuth2AccessToken accessToken, OAuth2Authentication authentication) { Map<String, Object> additionalInfo = new HashMap<>(); additionalInfo.put("organization", authentication.getName() + randomAlphabetic(4)); ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo); return accessToken; } }

 

然后,我們將把它連接到我們的授權服務器配置 - 如下所示:

@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); tokenEnhancerChain.setTokenEnhancers( Arrays.asList(tokenEnhancer(), accessTokenConverter())); endpoints.tokenStore(tokenStore()) .tokenEnhancer(tokenEnhancerChain) .authenticationManager(authenticationManager); } @Bean public TokenEnhancer tokenEnhancer() { return new CustomTokenEnhancer(); }

 

有了這個新的配置啟動和運行 - 這是一個令牌令牌有效載荷看起來像:

{
    "user_name": "john", "scope": [ "foo", "read", "write" ], "organization": "johnIiCh", "exp": 1458126622, "authorities": [ "ROLE_USER" ], "jti": "e0ad1ef3-a8a5-4eef-998d-00b26bc2c53f", "client_id": "fooClientIdPassword" }

 

5.1、在JS客戶端使用訪問令牌

最后,我們要在AngualrJS客戶端應用程序中使用令牌信息。我們將使用angular-jwt庫。 所以我們要做的就是在index.html中使用“組織”聲明:

<p class="navbar-text navbar-right">{{organization}}</p> <script type="text/javascript" src="https://cdn.rawgit.com/auth0/angular-jwt/master/dist/angular-jwt.js"> </script> <script> var app = angular.module('myApp', ["ngResource","ngRoute", "ngCookies", "angular-jwt"]); app.controller('mainCtrl', function($scope, $cookies, jwtHelper,...) { $scope.organiztion = ""; function getOrganization(){ var token = $cookies.get("access_token"); var payload = jwtHelper.decodeToken(token); $scope.organization = payload.organization; } ... });

6、不對稱的KeyPair

在我們以前的配置中,我們使用對稱密鑰來簽署我們的令牌:

@Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey("123"); return converter; }

 

我們還可以使用非對稱密鑰(公鑰和私鑰)來執行簽名過程。

6.1、生成JKS Java KeyStore文件

我們首先使用命令行工具keytool生成密鑰 - 更具體地說.jks文件:

keytool -genkeypair -alias mytest 
                    -keyalg RSA -keypass mypass -keystore mytest.jks -storepass mypass

 

該命令將生成一個名為mytest.jks的文件,其中包含我們的密鑰 - 公鑰和私鑰。 還要確保keypass和storepass是一樣的。

6.2、導出公鑰

接下來,我們需要從生成的JKS中導出我們的公鑰,我們可以使用下面的命令來實現:

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

示例回應如下所示:

-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgIK2Wt4x2EtDl41C7vfp OsMquZMyOyteO2RsVeMLF/hXIeYvicKr0SQzVkodHEBCMiGXQDz5prijTq3RHPy2 /5WJBCYq7yHgTLvspMy6sivXN7NdYE7I5pXo/KHk4nz+Fa6P3L8+L90E/3qwf6j3 DKWnAgJFRY8AbSYXt1d5ELiIG1/gEqzC0fZmNhhfrBtxwWXrlpUDT0Kfvf0QVmPR xxCLXT+tEe1seWGEqeOLL5vXRLqmzZcBe1RZ9kQQm43+a9Qn5icSRnDfTAesQ3Cr lAWJKl2kcWU1HwJqw+dZRSZ1X4kEXNMyzPdPBbGmU6MHdhpywI7SKZT7mX4BDnUK eQIDAQAB -----END PUBLIC KEY----- -----BEGIN CERTIFICATE----- MIIDCzCCAfOgAwIBAgIEGtZIUzANBgkqhkiG9w0BAQsFADA2MQswCQYDVQQGEwJ1 czELMAkGA1UECBMCY2ExCzAJBgNVBAcTAmxhMQ0wCwYDVQQDEwR0ZXN0MB4XDTE2 MDMxNTA4MTAzMFoXDTE2MDYxMzA4MTAzMFowNjELMAkGA1UEBhMCdXMxCzAJBgNV BAgTAmNhMQswCQYDVQQHEwJsYTENMAsGA1UEAxMEdGVzdDCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAICCtlreMdhLQ5eNQu736TrDKrmTMjsrXjtkbFXj Cxf4VyHmL4nCq9EkM1ZKHRxAQjIhl0A8+aa4o06t0Rz8tv+ViQQmKu8h4Ey77KTM urIr1zezXWBOyOaV6Pyh5OJ8/hWuj9y/Pi/dBP96sH+o9wylpwICRUWPAG0mF7dX eRC4iBtf4BKswtH2ZjYYX6wbccFl65aVA09Cn739EFZj0ccQi10/rRHtbHlhhKnj iy+b10S6ps2XAXtUWfZEEJuN/mvUJ+YnEkZw30wHrENwq5QFiSpdpHFlNR8CasPn WUUmdV+JBFzTMsz3TwWxplOjB3YacsCO0imU+5l+AQ51CnkCAwEAAaMhMB8wHQYD VR0OBBYEFOGefUBGquEX9Ujak34PyRskHk+WMA0GCSqGSIb3DQEBCwUAA4IBAQB3 1eLfNeq45yO1cXNl0C1IQLknP2WXg89AHEbKkUOA1ZKTOizNYJIHW5MYJU/zScu0 yBobhTDe5hDTsATMa9sN5CPOaLJwzpWV/ZC6WyhAWTfljzZC6d2rL3QYrSIRxmsp /J1Vq9WkesQdShnEGy7GgRgJn4A8CKecHSzqyzXulQ7Zah6GoEUD+vjb+BheP4aN hiYY1OuXD+HsdKeQqS+7eM5U7WW6dz2Q8mtFJ5qAxjY75T0pPrHwZMlJUhUZ+Q2V FfweJEaoNB9w9McPe1cAiE+oeejZ0jq0el3/dJsx3rlVqZN+lMhRJJeVHFyeb3XF lLFCUGhA7hxn2xf3x1JW -----END CERTIFICATE-----

 

我們只取得我們的公鑰,並將其復制到我們的資源服務器src / main / resources / public.txt中

-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgIK2Wt4x2EtDl41C7vfp OsMquZMyOyteO2RsVeMLF/hXIeYvicKr0SQzVkodHEBCMiGXQDz5prijTq3RHPy2 /5WJBCYq7yHgTLvspMy6sivXN7NdYE7I5pXo/KHk4nz+Fa6P3L8+L90E/3qwf6j3 DKWnAgJFRY8AbSYXt1d5ELiIG1/gEqzC0fZmNhhfrBtxwWXrlpUDT0Kfvf0QVmPR xxCLXT+tEe1seWGEqeOLL5vXRLqmzZcBe1RZ9kQQm43+a9Qn5icSRnDfTAesQ3Cr lAWJKl2kcWU1HwJqw+dZRSZ1X4kEXNMyzPdPBbGmU6MHdhpywI7SKZT7mX4BDnUK eQIDAQAB -----END PUBLIC KEY-----

 

6.3、Maven 配置

接下來,我們不希望JMS文件被maven過濾進程拾取 - 所以我們將確保將其排除在pom.xml中:

<build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <excludes> <exclude>*.jks</exclude> </excludes> </resource> </resources> </build>

 

如果我們使用Spring Boot,我們需要確保我們的JKS文件通過Spring Boot Maven插件添加到應用程序classpath - addResources:

<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <addResources>true</addResources> </configuration> </plugin> </plugins> </build>

6.4、授權服務器

現在,我們將配置JwtAccessTokenConverter使用mytest.jks中的KeyPair,如下所示:

@Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("mytest.jks"), "mypass".toCharArray()); converter.setKeyPair(keyStoreKeyFactory.getKeyPair("mytest")); return converter; }

6.5、資源服務器

最后,我們需要配置我們的資源服務器使用公鑰 - 如下所示:

@Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); Resource resource = new ClassPathResource("public.txt"); String publicKey = null; try { publicKey = IOUtils.toString(resource.getInputStream()); } catch (final IOException e) { throw new RuntimeException(e); } converter.setVerifierKey(publicKey); return converter; }

7、結論

放棄吧~~

 

 

【原創作者信息】

官網http://guan2ye.com
CSDN地址http://blog.csdn.net/chenjianandiyi
簡書地址http://www.jianshu.com/u/9b5d1921ce34
githubhttps://github.com/javanan
碼雲地址https://gitee.com/jamen/

 

 

 


免責聲明!

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



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