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
@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中使用对称密钥来签署我们的令牌 - 这意味着我们还需要为Resources Server使用相同的密钥

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.令牌中的自定义Claims

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

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

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

在下面的示例中,我们将使用此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 Client中的Access Token

最后,我们希望在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.访问资源服务器上的额外声明

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

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

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并使用访问令牌声明设置身份验证详细信息:

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

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

6.2。配置JwtTokenStore

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

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfigJwt
extends ResourceServerConfigurerAdapter { @Autowired
private CustomAccessTokenConverter customAccessTokenConverter; @Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
} @Bean
public 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 { @Autowired
private JwtTokenStore tokenStore; @Test
public 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

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

@Bean
public 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-----
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-----

或者,我们可以通过添加-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 - 如下所示:

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

7.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;
}

8.结论

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

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

将JWT与Spring Security OAuth结合使用的更多相关文章

  1. Using JWT with Spring Security OAuth

    http://www.baeldung.com/spring-security-oauth-jwt ************************************************** ...

  2. Spring Security OAuth 2.0

    续·前一篇<OAuth 2.0> OAuth 2.0 Provider 实现 在OAuth 2.0中,provider角色事实上是把授权服务和资源服务分开,有时候它们也可能在同一个应用中, ...

  3. Spring Security OAuth笔记

    因为工作需要,系统权限安全方面可能要用到Spring Security OAuth2.0,所以,近几天了解了一下OAuth相关的东西.目前好像还没有系统的学习资料,学习主要是通过博客,内容都是大同小异 ...

  4. Spring Security OAuth 2开发者指南译

    Spring Security OAuth 2开发者指南译 介绍 这是用户指南的支持OAuth 2.0.对于OAuth 1.0,一切都是不同的,所以看到它的用户指南. 本用户指南分为两部分,第一部分为 ...

  5. Spring Security + OAuth系统环境搭建(一)

    最近在做权限管理系统的重构工作,系统基于Spring Security + OAuth架构,整体架构.技术和之前调研的结果差不多,架构调研时有在这篇博客做过简单记录“Spring Cloud微服务下的 ...

  6. JWT和Spring Security集成

    通常情况下,把API直接暴露出去是风险很大的, 我们一般需要对API划分出一定的权限级别,然后做一个用户的鉴权,依据鉴权结果给予用户对应的API (一)JWT是什么,为什么要使用它? 互联网服务离不开 ...

  7. 【微服务】 数据库案例理解Spring Security OAuth

    突然被问,你是做技术的怎么不走技术路线呢?是啊~仔细想想至今做了这么多年的技术,研发过的系统&产品五花八门,涉及到的领域各行各业:政府.军队.公安.国安.石油&石化.金融.教育.华为等 ...

  8. Spring Security OAuth 格式化 token 输出

    个性化token 背景 上一篇文章<Spring Security OAuth 个性化token(一)>有提到,oauth2.0 接口默认返回的报文格式如下: {     "ac ...

  9. Spring Security OAuth 笔记

    1  单点登录 关于单点登录的原理,我觉得下面这位老哥讲的比较清楚,有兴趣可以看一下,下面我把其中的重点在此做个笔记总结 https://juejin.cn/post/6844904079274197 ...

随机推荐

  1. C语言中的文件操作

    按照字符的方式读取文件 按照行的方式读取文件 按照数据块的方式读取文件 按照格式化的方式读取文件 文件分类 记录文件:具有一定的结构记录组成,分为定长和不定长两种方式 流式文件:按照一个字符一个字符( ...

  2. 2015推荐的Android框架

    一.Guava Google的基于java1.6的类库集合的扩展项目,包括collections, caching, primitives support, concurrency libraries ...

  3. 谈"零缺陷"

    在刚参加工作初期的一次关于质量的培训中,第一次听到"零缺陷"这个词懵懵懂懂,当成一道概念题给记下.今年重读<质量免费>时对与零缺陷的部分始终心存疑虑,最近读<第一 ...

  4. ACM学习历程—ZOJ 3861 Valid Pattern Lock(dfs)

    Description Pattern lock security is generally used in Android handsets instead of a password. The p ...

  5. asm 兼容性、asm 主要参数管理

    一 ASM instance 与 Database instance 的版本兼容性说明 1. Oracle 11gR2 的ASM 支持11g和10g的数据库实例.但是在Oracle Clusterwa ...

  6. Elasticsearch官方安装

    Installationedit Elasticsearch requires at least Java 8. Specifically as of this writing, it is reco ...

  7. Python创建删除文件

    Python代码如下: import os directory = "E:\\学习日志\\" os.chdir(directory) # 改变当前工作目录 cwd = os.get ...

  8. FZU 2057 家谱(dfs)

    Problem 2057 家谱 Accept: 129    Submit: 356Time Limit: 1000 mSec    Memory Limit : 32768 KB  Problem ...

  9. JS判断上传文件类型

    /*   * 判断图片类型   */    function checkImgType(ths){        if (ths.value == "") {            ...

  10. MVC实用笔记

    ---------------------------- 渲染一个Action:@{Html.RenderAction("Rulelist", "AjaxReuqestD ...