Spring Boot 2 之 OAuth2 Auth和资源服务器

在此Spring安全性oauth2教程中,学习构建授权服务器以对您的身份进行身份验证以提供access_token,您可以使用该请求来向资源服务器请求数据。

1.概述

OAuth 2是一种授权方法,用于通过HTTP协议提供对受保护资源的访问。首先,oauth2使第三方应用程序可以获得对HTTP服务的有限访问权限–

  • 通过协调资源所有者和HTTP服务之间的批准交互来代表资源所有者
  • 或允许第三方应用程序代表自己获取访问权限。

1.1。的角色

OAuth定义了四个角色–

  • 资源所有者 –应用程序的用户。
  • 客户端 –需要访问资源服务器上用户数据的应用程序(用户正在使用)。
  • 资源服务器 –存储用户数据和http服务,这些服务可以将用户数据返回到经过身份验证的客户端。
  • 授权服务器 –负责验证用户的身份并提供授权令牌。资源服务器接受此令牌并验证您的身份。
Oauth2 Stream 程
Oauth2 Stream 程

1.2。访问令牌与刷新令牌

一个访问令牌是表示向客户授权的字符串。令牌代表资源所有者授予并由资源服务器和授权服务器强制执行的特定访问范围和持续时间。

授权服务器向客户端发布刷新令牌(以及访问令牌),当当前访问令牌无效或过期时,刷新令牌用于获取新的访问令牌,或者用于获取范围相同或范围较小的其他访问令牌(访问令牌)可能比资源所有者授权的生存期更短,权限也更少。授权服务器可以决定是否发出刷新令牌。

  • 访问令牌的责任是在数据过期之前访问数据。
  • 当现有访问令牌过期时,刷新令牌的职责是请求新的访问令牌。

2。 Oauth2 –授权服务器

要使用spring security oauth2模块创建授权服务器,我们需要使用注解@EnableAuthorizationServer并扩展类AuthorizationServerConfigurerAdapter

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter 
{
	@Autowired
	private BCryptPasswordEncoder passwordEncoder;

	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
		security
			.tokenKeyAccess("permitAll()")
			.checkTokenAccess("isAuthenticated()")
			.allowFormAuthenticationForClients();
	}

	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		clients
			.inMemory()
			.withClient("clientapp").secret(passwordEncoder.encode("123456"))
			.authorizedGrantTypes("password", "authorization_code", "refresh_token")
			.authorities("READ_ONLY_CLIENT")
			.scopes("read_profile_info")
			.resourceIds("oauth2-resource")
			.redirectUris("http://localhost:8081/login")
			.accessTokenValiditySeconds(120)
			.refreshTokenValiditySeconds(240000);
	}
}
  • Spring安全性oauth公开了两个用于检查令牌(/oauth/check_token/oauth/token_key)的端点,这些端点默认情况下受保护denyAll()tokenKeyAccess()checkTokenAccess()方法打开这些端点以供使用。
  • ClientDetailsServiceConfigurer用于定义客户端详细信息服务的内存中或JDBC实现。我们使用了内存实现。它具有以下重要属性:clientId –(必填)客户端ID。
    机密-(对于受信任的客户端是必需的)客户端机密(如果有)。
    范围–客户所限制的范围。如果范围未定义或为空(默认值),则客户端不受范围的限制。
    authorizedGrantTypes –授权给客户端使用的授权类型。默认值为空。
    权威–授予客户端的权威(常规的Spring Security权威)。
    redirectUris –将用户代理重定向到客户端的重定向端点。它必须是一个绝对URL。

3。 Oauth2 –资源服务器

要创建资源服务器组件,请使用@EnableResourceServer注解并扩展ResourceServerConfigurerAdapter类。

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;

@Configuration
@EnableResourceServer
public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter 
{
	@Override
	public void configure(HttpSecurity http) throws Exception {
		http
        	.authorizeRequests()
        	.antMatchers("/api/**").authenticated()
        	.antMatchers("/").permitAll();
	}
}

在config上面,在启动的所有端点上启用保护/api。其他所有端点均可自由访问。

资源服务器还提供了一种对用户本身进行身份验证的机制。在大多数情况下,它将是基于表单的登录名。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    	http
        	.antMatcher("/**")
	        	.authorizeRequests()
	        	.antMatchers("/oauth/authorize**", "/login**", "/error**")
	        	.permitAll()
        	.and()
            	.authorizeRequests()
            	.anyRequest().authenticated()
        	.and()
        		.formLogin().permitAll();
    }
 
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
        	.inMemoryAuthentication()
            .withUser("humptydumpty").password(passwordEncoder().encode("123456")).roles("USER");
    }
     
    @Bean
    public BCryptPasswordEncoder passwordEncoder(){ 
        return new BCryptPasswordEncoder(); 
    }
}

WebSecurityConfigurerAdapter类上方,设置基于表单的登录页面,并使用来打开授权URL permitAll()

4。 Oauth2保护的REST资源

出于演示目的,我仅创建了一个API,该API返回登录的用户名和电子邮件。

import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class RestResource 
{
	@RequestMapping("/api/users/me")
	public ResponseEntity<UserProfile> profile() 
	{
		//Build some dummy data to return for testing
		User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		String email = user.getUsername() + "@how2codex.com";

		UserProfile profile = new UserProfile();
		profile.setName(user.getUsername());
		profile.setEmail(email);

		return ResponseEntity.ok(profile);
	}
}
public class UserProfile 
{
	private String name;
	private String email;

	//Setters and getters

	@Override
	public String toString() {
		return "UserProfile [name=" + name + ", email=" + email + "]";
	}
}

5.演示

我们有一个API http://localhost:8080/api/users/me,可以通过直接在登录名中输入用户名/密码来访问该API,但是第三方应用程序无法像在浏览器中那样访问该API。他们需要oauth2令牌。

5.1。从用户获取授权授权代码

如上面的序列图所示,第一步是从URL获得资源所有者的授权授予: http://localhost:8080/oauth/authorize?client_id=clientapp&response_type=code&scope=read_profile_info

它将带来一个登录页面。提供用户名和密码。对于此演示,请使用“ humptydumpty”和“ 123456”。

登录页面
登录页面

登录后,您将被重定向到“授予访问权限”页面,您可以在其中选择授予对第三方应用程序的访问权限。

获得授权补助
获得授权补助

它将重定向到以下网址:http://localhost:8081/login?code=EAR76A。这'EAR76A'是第三方应用程序的授权代码。

5.2。从授权服务器获取访问令牌

现在,应用程序将使用授权授予来获取访问令牌。在这里,我们需要提出以下要求。使用此处第一步中获得的代码。

http://localhost:8080/oauth/token

Headers:

Content-Type: application/x-www-form-urlencoded
authorization: Basic Y2xpZW50YXBwOjEyMzQ1Ng==

Form data - application/x-www-form-urlencoded:

grant_type=authorization_code
code=EAR76A
redirect_uri=http://localhost:8081/login

它将在单独的窗口中查询客户端应用程序凭据。

客户端验证
客户端验证

或从cURL发出类似请求。

curl -X POST --user clientapp:123456 http://localhost:8081/oauth/token 
		-H "content-type: application/x-www-form-urlencoded" 
		-d "code=FfrzTj&grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%3A8082%2Flogin&scope=read_user_info"
{
	"access_token": "59ddb16b-6943-42f5-8e2f-3acb23f8e3c1",
	"token_type": "bearer",
	"refresh_token": "cea0aa8f-f732-44fc-8ba3-5e868d94af64",
	"expires_in": 4815,
	"scope": "read_profile_info"
}

阅读更多:如何在Windows中执行cURL命令

5.3。从资源服务器访问用户数据

获得访问令牌后,我们可以转到资源服务器以获取受保护的用户数据。

达到以下要求:

curl -X GET http://localhost:8080/api/users/me 
	 -H "authorization: Bearer 59ddb16b-6943-42f5-8e2f-3acb23f8e3c1"

它将返回响应。

{"name":"humptydumpty","email":"humptydumpty@how2codex.com"}

6。 Spring Security oauth2应用程序的Maven依赖项

spring security 5 oauth2示例使用的pom文件是:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.4.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.how2codex</groupId>
	<artifactId>spring-oauth2-resource-server-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-oauth2-resource-server-demo</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.security.oauth.boot</groupId>
			<artifactId>spring-security-oauth2-autoconfigure</artifactId>
			<version>2.1.8.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

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

</project>

将您的问题留在我的评论中。

参考文献:

Oauth2协议
OAuth2自动配置

saigon has written 1445 articles

Leave a Reply