900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > 将JWT与Spring Security OAuth结合使用

将JWT与Spring Security OAuth结合使用

时间:2021-03-13 12:15:23

相关推荐

将JWT与Spring Security OAuth结合使用

1.概述

在本教程中,我们将讨论如何使用Spring Security OAuth2实现来使用JSON Web令牌

我们还将继续构建此OAuth系列的上一篇文章

2. Maven配置

首先,我们需要在我们的pom.xml中添加spring-security-jwt依赖项:

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

请注意,我们需要将spring-security-jwt依赖项添加到Authorization Server和Resource Server

3.授权服务器

接下来,我们将配置我们的授权服务器以使用JwtTokenStore - 如下所示:

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

请注意,我们在JwtAccessTokenConverter中使用对称密钥来签署我们的令牌 - 这意味着我们还需要为Resources Server使用相同的密钥

4.资源服务器

现在,让我们看看我们的资源服务器配置 - 这与授权服务器的配置非常相似:

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

请记住,我们将这两个服务器定义为完全独立且可独立部署。这就是我们需要在新配置中再次声明一些相同bean的原因。

5.令牌中的自定义Claims

现在让我们设置一些基础设施,以便能够在Access Token中添加一些自定义声明

框架提供的标准声明都很好,但大多数时候我们需要在令牌中使用一些额外的信息才能在客户端使用。框架提供的标准声明都很好,但大多数时候我们需要在令牌中使用一些额外的信息才能在客户端使用

我们将定义TokenEnhancer以使用这些附加声明来自定义我们的访问令牌

在下面的示例中,我们将使用此CustomTokenEnhancer向我们的访问令牌添加一个额外的字段“组织”

public class CustomTokenEnhancer implements TokenEnhancer {@Overridepublic 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;}}

然后,我们将它连接到我们的授权服务器配置 - 如下所示:

@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));endpoints.tokenStore(tokenStore()).tokenEnhancer(tokenEnhancerChain).authenticationManager(authenticationManager);}@Beanpublic 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 Client中的Access Token

最后,我们希望在AngualrJS客户端应用程序中使用令牌信息。我们将使用angular-jwt库。

那么我们要做的就是在index.html中使用“组织”声明:

<p class="navbar-text navbar-right">{{organization}}</p><script type="text/javascript"src="/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,...) {$aniztion = "";function getOrganization(){var token = $cookies.get("access_token");var payload = jwtHelper.decodeToken(token);$anization = anization;}...});

6.访问资源服务器上的额外声明

但是,我们如何在资源服务器端访问该信息?

我们在这里做的是 -从访问令牌中提取额外的声明

public Map<String, Object> getExtraInfo(OAuth2Authentication auth) {OAuth2AuthenticationDetails details= (OAuth2AuthenticationDetails) auth.getDetails();OAuth2AccessToken accessToken = tokenStore.readAccessToken(details.getTokenValue());return accessToken.getAdditionalInformation();}

在下一节中,我们将讨论如何使用自定义AccessTokenConverter将这些额外信息添加到我们的身份验证详细信息中

6.1。自定义AccessTokenConverter

让我们创建CustomAccessTokenConverter并使用访问令牌声明设置身份验证详细信息:

@Componentpublic class CustomAccessTokenConverter extends DefaultAccessTokenConverter {@Overridepublic OAuth2Authentication extractAuthentication(Map<String, ?> claims) {OAuth2Authentication authentication= super.extractAuthentication(claims);authentication.setDetails(claims);return authentication;}}

注意:DefaultAccessTokenConverter用于将身份验证详细信息设置为Null

6.2。配置JwtTokenStore

接下来,我们将配置我们的JwtTokenStore以使用我们的CustomAccessTokenConverter:

@Configuration@EnableResourceServerpublic class OAuth2ResourceServerConfigJwtextends ResourceServerConfigurerAdapter {@Autowiredprivate CustomAccessTokenConverter customAccessTokenConverter;@Beanpublic TokenStore tokenStore() {return new JwtTokenStore(accessTokenConverter());}@Beanpublic JwtAccessTokenConverter accessTokenConverter() {JwtAccessTokenConverter converter = new JwtAccessTokenConverter();converter.setAccessTokenConverter(customAccessTokenConverter);}// ...}

6.3。身份验证对象中提供的额外声明

既然Authorization Server在令牌中添加了一些额外的声明,我们现在可以直接在Authentication对象中在Resource Server端访问

public Map<String, Object> getExtraInfo(Authentication auth) {OAuth2AuthenticationDetails oauthDetails= (OAuth2AuthenticationDetails) auth.getDetails();return (Map<String, Object>) oauthDetails.getDecodedDetails();}

6.4。验证测试

让我们确保我们的Authentication对象包含额外信息:

@RunWith(SpringRunner.class)@SpringBootTest(classes = ResourceServerApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)public class AuthenticationClaimsIntegrationTest {@Autowiredprivate JwtTokenStore tokenStore;@Testpublic void whenTokenDoesNotContainIssuer_thenSuccess() {String tokenValue = obtainAccessToken("fooClientIdPassword", "john", "123");OAuth2Authentication auth = tokenStore.readAuthentication(tokenValue);Map<String, Object> details = (Map<String, Object>) auth.getDetails();assertTrue(details.containsKey("organization"));}private String obtainAccessToken(String clientId, String username, String password) {Map<String, String> params = new HashMap<>();params.put("grant_type", "password");params.put("client_id", clientId);params.put("username", username);params.put("password", password);Response response = RestAssured.given().auth().preemptive().basic(clientId, "secret").and().with().params(params).when().post("http://localhost:8081/spring-security-oauth-server/oauth/token");return response.jsonPath().getString("access_token");}}

注意:我们从授权服务器获得了具有额外声明的访问令牌,然后我们从中读取了Authentication对象,其中包含详细信息对象中的额外信息“organization”

7.非对称KeyPair

在我们之前的配置中,我们使用对称密钥来签署我们的令牌:

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

我们还可以使用非对称密钥(公钥和私钥)来执行签名过程

7.1。生成JKS Java KeyStore文件

让我们首先使用命令行工具keytool生成密钥 - 更具体地说是.jks文件

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

该命令将生成一个名为mytest.jks的文件,其中包含我们的密钥 - 公钥和私钥

还要确保keypass和storepass是相同的。

7.2。导出公钥

接下来,我们需要从生成的JKS导出我们的公钥,我们可以使用以下命令来执行此操作:

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

示例响应将如下所示:

-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgIK2Wt4x2EtDl41C7vfpOsMquZMyOyteO2RsVeMLF/hXIeYvicKr0SQzVkodHEBCMiGXQDz5prijTq3RHPy2/5WJBCYq7yHgTLvspMy6sivXN7NdYE7I5pXo/KHk4nz+Fa6P3L8+L90E/3qwf6j3DKWnAgJFRY8AbSYXt1d5ELiIG1/gEqzC0fZmNhhfrBtxwWXrlpUDT0Kfvf0QVmPRxxCLXT+tEe1seWGEqeOLL5vXRLqmzZcBe1RZ9kQQm43+a9Qn5icSRnDfTAesQ3CrlAWJKl2kcWU1HwJqw+dZRSZ1X4kEXNMyzPdPBbGmU6MHdhpywI7SKZT7mX4BDnUKeQIDAQAB-----END PUBLIC KEY----------BEGIN CERTIFICATE-----MIIDCzCCAfOgAwIBAgIEGtZIUzANBgkqhkiG9w0BAQsFADA2MQswCQYDVQQGEwJ1czELMAkGA1UECBMCY2ExCzAJBgNVBAcTAmxhMQ0wCwYDVQQDEwR0ZXN0MB4XDTE2MDMxNTA4MTAzMFoXDTE2MDYxMzA4MTAzMFowNjELMAkGA1UEBhMCdXMxCzAJBgNVBAgTAmNhMQswCQYDVQQHEwJsYTENMAsGA1UEAxMEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAICCtlreMdhLQ5eNQu736TrDKrmTMjsrXjtkbFXjCxf4VyHmL4nCq9EkM1ZKHRxAQjIhl0A8+aa4o06t0Rz8tv+ViQQmKu8h4Ey77KTMurIr1zezXWBOyOaV6Pyh5OJ8/hWuj9y/Pi/dBP96sH+o9wylpwICRUWPAG0mF7dXeRC4iBtf4BKswtH2ZjYYX6wbccFl65aVA09Cn739EFZj0ccQi10/rRHtbHlhhKnjiy+b10S6ps2XAXtUWfZEEJuN/mvUJ+YnEkZw30wHrENwq5QFiSpdpHFlNR8CasPnWUUmdV+JBFzTMsz3TwWxplOjB3YacsCO0imU+5l+AQ51CnkCAwEAAaMhMB8wHQYDVR0OBBYEFOGefUBGquEX9Ujak34PyRskHk+WMA0GCSqGSIb3DQEBCwUAA4IBAQB31eLfNeq45yO1cXNl0C1IQLknP2WXg89AHEbKkUOA1ZKTOizNYJIHW5MYJU/zScu0yBobhTDe5hDTsATMa9sN5CPOaLJwzpWV/ZC6WyhAWTfljzZC6d2rL3QYrSIRxmsp/J1Vq9WkesQdShnEGy7GgRgJn4A8CKecHSzqyzXulQ7Zah6GoEUD+vjb+BheP4aNhiYY1OuXD+HsdKeQqS+7eM5U7WW6dz2Q8mtFJ5qAxjY75T0pPrHwZMlJUhUZ+Q2VFfweJEaoNB9w9McPe1cAiE+oeejZ0jq0el3/dJsx3rlVqZN+lMhRJJeVHFyeb3XFlLFCUGhA7hxn2xf3x1JW-----END CERTIFICATE-----

我们只使用我们的公钥并将其复制到我们的资源服务器src / main / resources / public.txt:

-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgIK2Wt4x2EtDl41C7vfpOsMquZMyOyteO2RsVeMLF/hXIeYvicKr0SQzVkodHEBCMiGXQDz5prijTq3RHPy2/5WJBCYq7yHgTLvspMy6sivXN7NdYE7I5pXo/KHk4nz+Fa6P3L8+L90E/3qwf6j3DKWnAgJFRY8AbSYXt1d5ELiIG1/gEqzC0fZmNhhfrBtxwWXrlpUDT0Kfvf0QVmPRxxCLXT+tEe1seWGEqeOLL5vXRLqmzZcBe1RZ9kQQm43+a9Qn5icSRnDfTAesQ3CrlAWJKl2kcWU1HwJqw+dZRSZ1X4kEXNMyzPdPBbGmU6MHdhpywI7SKZT7mX4BDnUKeQIDAQAB-----END PUBLIC KEY-----

或者,我们可以通过添加-noout参数来仅导出公钥:

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

7.3。 Maven配置

接下来,我们不希望maven过滤过程接收JKS文件 - 因此我们确保将其排除在pom.xml中:

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

如果我们使用Spring Boot,我们需要确保通过Spring Boot Maven插件将我们的JKS文件添加到应用程序类路径中 - addResources

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

7.4。授权服务器

现在,我们将配置JwtAccessTokenConverter以使用mytest.jks中的KeyPair - 如下所示:

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

7.5。资源服务器

最后,我们需要配置我们的资源服务器以使用公钥 - 如下所示:

@Beanpublic 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;}

8.结论

在这篇快速文章中,我们专注于使用JSON Web Tokens设置Spring Security OAuth2项目

可以在github项目中找到本教程的完整实现 - 这是一个基于Eclipse的项目,因此它应该很容易导入和运行。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。