拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Spring Security OAuth授权服务器

Spring Security OAuth授权服务器

白鹭 - 2021-11-24 710 0 0

1.简介

OAuth是描述授权过程的开放标准。它可用于授权用户访问API。例如,REST API可以仅限制具有适当角色的注册用户的访问。

OAuth授权服务器负责对用户进行身份验证,并发布包含用户数据和适当访问策略的访问令牌。

在本教程中,我们将使用Spring Security OAuth Authorization Server实验模块实现一个简单的OAuth服务器。

在此过程中,我们将创建一个客户端服务器应用程序,该应用程序将从REST API中获取Tutorial文章列表。客户端服务和服务器服务都将需要OAuth身份验证。

2.授权服务器实施

让我们从查看OAuth授权服务器配置开始。它将用作文章资源和客户端服务器的身份验证源。

2.1依赖关系

首先,我们需要在pom.xml文件中添加一些依赖关系:

<dependency>

 <groupId>org.springframework.boot</groupId>

 <artifactId>spring-boot-starter-web</artifactId>

 <version>2.4.3</version>

 </dependency>

 <dependency>

 <groupId>org.springframework.boot</groupId>

 <artifactId>spring-boot-starter-security</artifactId>

 <version>2.4.3</version>

 </dependency>

 <dependency>

 <groupId>org.springframework.security.experimental</groupId>

 <artifactId>spring-security-oauth2-authorization-server</artifactId>

 <version>0.1.0</version>

 </dependency>

2.2。配置

application.yml文件中server.port属性来配置身份验证服务器将运行的端口:

server:

 port: 9000

之后,我们可以转到Spring bean配置。首先,我们需要一个@Configuration类并导入OAuthAuthorizationServerConfiguration 。在配置类内部,我们将创建一些特定于OAuth的bean。第一个将是客户端服务的存储库。在我们的示例中,我们将使用RegisteredClient构建器类创建一个客户端:

@Configuration

 @Import(OAuth2AuthorizationServerConfiguration.class)

 public class AuthorizationServerConfig {

 @Bean

 public RegisteredClientRepository registeredClientRepository() {

 RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())

 .clientId("article-client")

 .clientSecret("secret")

 .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)

 .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)

 .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)

 .redirectUri("http://localhost:8080/login/oauth2/code/articles-client-oidc")

 .scope("articles.read")

 .build();

 return new InMemoryRegisteredClientRepository(registeredClient);

 }

 }

我们正在配置的属性是:

  • 客户端ID – Spring将使用它来标识哪个客户端正在尝试访问资源
  • 客户端密码-客户端和服务器已知的秘密,可在两者之间提供信任
  • 身份验证方法–在我们的示例中,我们将使用基本身份验证,即用户名和密码
  • 授权授予类型–我们希望允许客户端生成授权代码和刷新令牌
  • 重定向URI –客户端将在基于重定向的流中使用它
  • 范围–此参数定义客户端可能具有的授权。在我们的案例中,我们只有一篇– articles.read

每个授权服务器都需要其令牌的签名密钥,以在安全域之间保持适当的边界。让我们生成一个2048字节的RSA密钥:

@Bean

 public JWKSource<SecurityContext> jwkSource() {

 RSAKey rsaKey = generateRsa();

 JWKSet jwkSet = new JWKSet(rsaKey);

 return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);

 }



 private static RSAKey generateRsa() {

 KeyPair keyPair = generateRsaKey();

 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();

 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

 return new RSAKey.Builder(publicKey)

 .privateKey(privateKey)

 .keyID(UUID.randomUUID().toString())

 .build();

 }



 private static KeyPair generateRsaKey() {

 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");

 keyPairGenerator.initialize(2048);

 return keyPairGenerator.generateKeyPair();

 }

除了签名密钥,每个授权服务器还需要具有唯一的发行者URL。通过创建ProviderSettings bean,我们将其设置为端口9000 http://127.0.0.1

@Bean

 public ProviderSettings providerSettings() {

 return new ProviderSettings().issuer("http://127.0.0.1:9000");

 }

最后,我们将使用带有@EnableWebSecurity注释的配置类来启用Spring Web安全模块:

@EnableWebSecurity

 public class DefaultSecurityConfig {



 @Bean

 SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {

 http.authorizeRequests(authorizeRequests ->

 authorizeRequests.anyRequest().authenticated()

 )

 .formLogin(withDefaults());

 return http.build();

 }



 // ...

 }

在这里,我们正在调用authorizeRequests.anyRequest().authenticated()以要求对所有请求进行身份验证,并且通过调用formLogin(defaults())方法来提供基于表单的身份验证。

此外,我们将定义一组用于测试的示例用户。为了这个示例,让我们创建一个只有一个管理员用户的存储库:

@Bean

 UserDetailsService users() {

 UserDetails user = User.withDefaultPasswordEncoder()

 .username("admin")

 .password("password")

 .build();

 return new InMemoryUserDetailsManager(user);

 }

3.资源服务器

现在,我们将创建一个资源服务器,该服务器将从GET端点返回文章列表。端点应仅允许根据我们的OAuth服务器进行身份验证的请求。

3.1依赖关系

首先,让我们包括所需的依赖项:

<dependency>

 <groupId>org.springframework.boot</groupId>

 <artifactId>spring-boot-starter-web</artifactId>

 <version>2.4.3</version>

 </dependency>

 <dependency>

 <groupId>org.springframework.boot</groupId>

 <artifactId>spring-boot-starter-security</artifactId>

 <version>2.4.3</version>

 </dependency>

 <dependency>

 <groupId>org.springframework.boot</groupId>

 <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>

 <version>2.4.3</version>

 </dependency>

3.2 配置

在开始执行代码之前,我们应该在application.yml文件中配置一些属性。第一个是服务器端口:

server:

 port: 8090

接下来,是时候进行安全配置了。 ProviderSettings bean中配置的主机和端口为身份验证服务器设置正确的URL:

spring:

 security:

 oauth2:

 resourceserver:

 jwt:

 issuer-uri: http://127.0.0.1:9000

现在,我们可以设置我们的Web安全配置。再次,我们要明确地说,对文章资源的每个请求都应被授权并具有适当的article.read权限:

@EnableWebSecurity

 public class ResourceServerConfig {



 @Bean

 SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

 http.mvcMatcher("/articles/**")

 .authorizeRequests()

 .mvcMatchers("/articles/**").access("hasAuthority('SCOPE_article.read')")

 .and()

 .oauth2ResourceServer()

 .jwt();

 return http.build();

 }

 }

如此处所示,我们还调用了oauth2ResourceServer()方法,该方法将基于application.yml配置来配置OAuth服务器连接。

3.3 Articles Controller

最后,我们将创建一个REST控制器,该控制器将返回GET /articles端点下的文章列表:

@RestController

 public class ArticlesController {



 @GetMapping("/articles")

 public String[] getArticles() {

 return new String[] { "Article 1", "Article 2", "Article 3" };

 }

 }

4. API客户端

对于最后一部分,我们将创建一个REST API客户端,该客户端将从资源服务器中获取文章列表。

4.1 依赖关系

首先,让我们包括所需的依赖项:

<dependency>

 <groupId>org.springframework.boot</groupId>

 <artifactId>spring-boot-starter-web</artifactId>

 <version>2.4.3</version>

 </dependency>

 <dependency>

 <groupId>org.springframework.boot</groupId>

 <artifactId>spring-boot-starter-security</artifactId>

 <version>2.4.3</version>

 </dependency>

 <dependency>

 <groupId>org.springframework.boot</groupId>

 <artifactId>spring-boot-starter-oauth2-client</artifactId>

 <version>2.4.3</version>

 </dependency>

 <dependency>

 <groupId>org.springframework</groupId>

 <artifactId>spring-webflux</artifactId>

 <version>5.3.4</version>

 </dependency>

 <dependency>

 <groupId>io.projectreactor.netty</groupId>

 <artifactId>reactor-netty</artifactId>

 <version>1.0.4</version>

 </dependency>

4.2 配置

正如我们之前所做的,我们将定义一些配置属性以用于身份验证:

server:

 port: 8080



 spring:

 security:

 oauth2:

 client:

 registration:

 articles-client-oidc:

 provider: spring

 client-id: articles-client

 client-secret: secret

 authorization-grant-type: authorization_code

 redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"

 scope: openid

 client-name: articles-client-oidc

 articles-client-authorization-code:

 provider: spring

 client-id: articles-client

 client-secret: secret

 authorization-grant-type: authorization_code

 redirect-uri: "{baseUrl}/authorized"

 scope: articles.read

 client-name: articles-client-authorization-code

 provider:

 spring:

 issuer-uri: http://127.0.0.1:9000

现在,让我们创建一个WebClient实例以执行对资源服务器的HTTP请求。我们将仅使用OAuth授权过滤器的一种附加的标准实现:

@Bean

 WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {

 ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =

 new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);

 return WebClient.builder()

 .apply(oauth2Client.oauth2Configuration())

 .build();

 }

WebClient需要OAuth2AuthorizedClientManager作为依赖项。让我们创建一个默认实现:

@Bean

 OAuth2AuthorizedClientManager authorizedClientManager(

 ClientRegistrationRepository clientRegistrationRepository,

 OAuth2AuthorizedClientRepository authorizedClientRepository) {



 OAuth2AuthorizedClientProvider authorizedClientProvider =

 OAuth2AuthorizedClientProviderBuilder.builder()

 .authorizationCode()

 .refreshToken()

 .build();

 DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(

 clientRegistrationRepository, authorizedClientRepository);

 authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);



 return authorizedClientManager;

 }

最后,我们将配置网络安全性:

@EnableWebSecurity

 public class SecurityConfig {



 @Bean

 SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

 http

 .authorizeRequests(authorizeRequests ->

 authorizeRequests.anyRequest().authenticated()

 )

 .oauth2Login(oauth2Login ->

 oauth2Login.loginPage("/oauth2/authorization/articles-client-oidc"))

 .oauth2Client(withDefaults());

 return http.build();

 }

 }

在这里,以及在其他服务器中,我们将需要对每个请求进行身份验证。另外,我们需要配置登录页面URL(在.yml config中定义)和OAuth客户端。

4.3 Articles Client Controller

最后,我们可以创建数据访问控制器。我们将使用先前配置的WebClient将HTTP请求发送到资源服务器:

@RestController

 public class ArticlesController {



 private WebClient webClient;



 @GetMapping(value = "/articles")

 public String[] getArticles(

 @RegisteredOAuth2AuthorizedClient("articles-client-authorization-code") OAuth2AuthorizedClient authorizedClient

 ) {

 return this.webClient

 .get()

 .uri("http://localhost:8090/articles")

 .attributes(oauth2AuthorizedClient(authorizedClient))

 .retrieve()

 .bodyToMono(String[].class)

 .block();

 }

 }

OAuth2AuthorizedClient类的形式从请求中获取OAuth授权令牌。它由Spring使用@RegisterdOAuth2AuthorizedClient批注(带有正确的标识)自动绑定。在我们的例子中,它是从我们先前在.yml文件中article-client-authorizaiton-code .yml

该授权令牌进一步传递到HTTP请求。

4.4 访问文章列表

现在,当我们进入浏览器并尝试访问http://localhost:8080/articles页面时,我们将自动重定向到http://127.0.0.1:900/login URL下的OAuth服务器登录页面:

Spring

提供正确的用户名和密码后,授权服务器会将我们重定向到所请求的URL(文章列表)。

由于访问令牌将存储在cookie中,因此对文章端点的其他请求将不需要登录。

5.结论

在本文中,我们学习了如何设置,配置和使用Spring Security OAuth授权服务器。

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *