9

Spring Authorization Server OAuth2授权服务器配置详解

 2 years ago
source link: https://blog.didispace.com/spring-authorization-server-config/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Spring Authorization Server OAuth2授权服务器配置详解

前两篇文章分别体验了Spring Authorization Server的使用。今天来讲讲Spring Authorization Server授权服务器的配置。强烈建议自己手动搭建一次试试,纸上得来终觉浅,深知此事要躬行。提升你的代码量才是提高编程技能的不二法门,这也是本篇教程的意义所在。同时也希望多多点赞,转发,多多支持作者原创分享。

首先要创建一个Spring Boot Servlet Web项目,这个不难就不赘述了。集成Spring Authorization Server需要引入:

<!--  spring security starter 必须  -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>0.2.0</version> <!-- 截至现在版本 -->
</dependency>

OAuth2.0 Client客户端需要注册到授权服务器并持久化,Spring Authorization Server提供了JDBC实现,参见JdbcRegisteredClientRepository。为了演示方便这里我采用了H2数据库,需要以下依赖:

<!--  jdbc 必须引入否则自行实现  -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>

生产你可以切换到其它关系型数据库,数据库脚本在Spring Authorization Server的使用一文的DEMO中。

Spring Authorization Server配置

接下来是Spring Authorization Server的配置。

过滤器链配置

根据上一文对过滤器链的拆解,我们需要在Spring Security的过滤器链中注入一些特定的过滤器。这些过滤器的配置由OAuth2AuthorizationServerConfigurer来完成。以下为默认的配置:

void defaultOAuth2AuthorizationServerConfigurer(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer<>();
// TODO 你可以根据需求对authorizationServerConfigurer进行一些个性化配置
RequestMatcher authorizationServerEndpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();

// 拦截 授权服务器相关的请求端点
http.requestMatcher(authorizationServerEndpointsMatcher)
.authorizeRequests().anyRequest().authenticated().and()
// 忽略掉相关端点的csrf
.csrf(csrf -> csrf.ignoringRequestMatchers(authorizationServerEndpointsMatcher))
// 开启form登录
.formLogin()
.and()
// 应用 授权服务器的配置
.apply(authorizationServerConfigurer);
}

你可以调用OAuth2AuthorizationServerConfigurer提供的配置方法进行一些个性化配置。

OAuth2.0客户端信息持久化

这些信息会持久化到数据库,Spring Authorization Server提供了三个DDL脚本。在入门教程Spring Authorization Server的使用的DEMO,H2会自动初始化执行这些DDL脚本,如果你切换到Mysql等数据库,可能需要你自行执行。

客户端配置信息注册

授权服务器要求客户端必须是已经注册的,避免非法的客户端发起授权申请。就像你平常去一些开放平台申请一个ClientIDSecret。下面是定义脚本:

CREATE TABLE oauth2_registered_client
(
id varchar(100) NOT NULL,
client_id varchar(100) NOT NULL,
client_id_issued_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
client_secret varchar(200) NULL,
client_secret_expires_at timestamp NULL,
client_name varchar(200) NOT NULL,
client_authentication_methods varchar(1000) NOT NULL,
authorization_grant_types varchar(1000) NOT NULL,
redirect_uris varchar(1000) NULL,
scopes varchar(1000) NOT NULL,
client_settings varchar(2000) NOT NULL,
token_settings varchar(2000) NOT NULL,
PRIMARY KEY (id)
);

对应的Java类为RegisteredClient:

public class RegisteredClient implements Serializable {

private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
private String id;
private String clientId;
private Instant clientIdIssuedAt;
private String clientSecret;
private Instant clientSecretExpiresAt;
private String clientName;
private Set<ClientAuthenticationMethod> clientAuthenticationMethods;
private Set<AuthorizationGrantType> authorizationGrantTypes;
private Set<String> redirectUris;
private Set<String> scopes;
private ClientSettings clientSettings;
private TokenSettings tokenSettings;

// 省略
}

定义一个客户端可以通过下面的Builder方法实现:

RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
// 唯一的客户端ID和密码
.clientId("felord-client")
.clientSecret("secret")
// 名称 可不定义
.clientName("felord")
// 授权方法
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
// 授权类型
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
// 回调地址名单,不在此列将被拒绝 而且只能使用IP或者域名 不能使用 localhost
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/felord-oidc")
.redirectUri("http://127.0.0.1:8080/authorized")
.redirectUri("http://127.0.0.1:8080/foo/bar")
.redirectUri("https://baidu.com")
// OIDC支持
.scope(OidcScopes.OPENID)
// 其它Scope
.scope("message.read")
.scope("message.write")
// JWT的配置项 包括TTL 是否复用refreshToken等等
.tokenSettings(TokenSettings.builder().build())
// 配置客户端相关的配置项,包括验证密钥或者 是否需要授权页面
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();

持久化到数据库的RegisteredClient用JSON表示为:

{
"id": "658cd010-4d8c-4824-a8c7-a86b642299af",
"client_id": "felord-client",
"client_id_issued_at": "2021-11-11 18:01:09",
"client_secret": "{bcrypt}$2a$10$XKZ8iUckDcdQWnqw682zV.DVyGuov8Sywx1KyAn4tySsw.Jtltg0.",
"client_secret_expires_at": null,
"client_name": "felord",
"client_authentication_methods": "client_secret_basic",
"authorization_grant_types": "refresh_token,client_credentials,authorization_code",
"redirect_uris": "http://127.0.0.1:8080/foo/bar,http://127.0.0.1:8080/authorized,http://127.0.0.1:8080/login/oauth2/code/felord-oidc,https://baidu.com",
"scopes": "openid,message.read,message.write",
"client_settings": "{\"@class\":\"java.util.Collections$UnmodifiableMap\",\"settings.client.require-proof-key\":false,\"settings.client.require-authorization-consent\":true}",
"token_settings": "{\"@class\":\"java.util.Collections$UnmodifiableMap\",\"settings.token.reuse-refresh-tokens\":true,\"settings.token.id-token-signature-algorithm\":[\"org.springframework.security.oauth2.jose.jws.SignatureAlgorithm\",\"RS256\"],\"settings.token.access-token-time-to-live\":[\"java.time.Duration\",300.000000000],\"settings.token.refresh-token-time-to-live\":[\"java.time.Duration\",3600.000000000]}"
}

注意上面的配置和你OAuth2.0客户端应用的配置息息相关。

既然持久化了,那自然需要操作该表的JDBC服务接口了,这个接口为RegisteredClientRepository。我们需要声明一个实现为Spring Bean,这里选择基于JDBC的实现:

@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
return new JdbcRegisteredClientRepository(jdbcTemplate);
}

别忘记调用save(RegisteredClient)方法把需要注册的客户端信息持久化。

该实现依赖spring-boot-starter-jdbc类库,你也可以闲得慌使用Mybatis进行实现。

OAuth2授权信息持久化

记录授权的资源拥有者(Resource Owner)对某个客户端的某次授权记录。对应的Java类为OAuth2Authorization。下面是定义脚本:

CREATE TABLE oauth2_authorization
(
id varchar(100) NOT NULL,
registered_client_id varchar(100) NOT NULL,
principal_name varchar(200) NOT NULL,
authorization_grant_type varchar(100) NOT NULL,
attributes varchar(4000) NULL,
state varchar(500) NULL,
authorization_code_value blob NULL,
`authorization_code_issued_at` timestamp NULL,
authorization_code_expires_at timestamp NULL,
authorization_code_metadata varchar(2000) NULL,
access_token_value blob NULL,
access_token_issued_at timestamp NULL,
access_token_expires_at timestamp NULL,
access_token_metadata varchar(2000) NULL,
access_token_type varchar(100) NULL,
access_token_scopes varchar(1000) NULL,
oidc_id_token_value blob NULL,
oidc_id_token_issued_at timestamp NULL,
oidc_id_token_expires_at timestamp NULL,
oidc_id_token_metadata varchar(2000) NULL,
refresh_token_value blob NULL,
refresh_token_issued_at timestamp NULL,
refresh_token_expires_at timestamp NULL,
refresh_token_metadata varchar(2000) NULL,
PRIMARY KEY (id)
);

这里的机制目前还没有研究,先挖个坑。

同样它也需要一个持久化服务接口OAuth2AuthorizationService并注入Spring IoC

/**
* 管理OAuth2授权信息服务
*
* @param jdbcTemplate the jdbc template
* @param registeredClientRepository the registered client repository
* @return the o auth 2 authorization service
*/
@Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
}

持久化到数据库的OAuth2Authorization用JSON表示为:

{
"id": "aa2f6e7d-d9b9-4360-91ef-118cbb6d4b09",
"registered_client_id": "658cd010-4d8c-4824-a8c7-a86b642299af",
"principal_name": "felord",
"authorization_grant_type": "authorization_code",
"attributes": "{\"@class\":\"java.util.Collections$UnmodifiableMap\",\"org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\":{\"@class\":\"org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\",\"authorizationUri\":\"http://localhost:9000/oauth2/authorize\",\"authorizationGrantType\":{\"value\":\"authorization_code\"},\"responseType\":{\"value\":\"code\"},\"clientId\":\"felord-client\",\"redirectUri\":\"http://127.0.0.1:8080/foo/bar\",\"scopes\":[\"java.util.Collections$UnmodifiableSet\",[\"message.read\",\"message.write\"]],\"state\":\"9gTcVNXgV8Pn_Ron3bkFb6M92AYCodeWKoEd6xxaiUg=\",\"additionalParameters\":{\"@class\":\"java.util.Collections$UnmodifiableMap\"},\"authorizationRequestUri\":\"http://localhost:9000/oauth2/authorize?response_type=code&client_id=felord-client&scope=message.read%20message.write&state=9gTcVNXgV8Pn_Ron3bkFb6M92AYCodeWKoEd6xxaiUg%3D&redirect_uri=http://127.0.0.1:8080/foo/bar\",\"attributes\":{\"@class\":\"java.util.Collections$UnmodifiableMap\"}},\"java.security.Principal\":{\"@class\":\"org.springframework.security.authentication.UsernamePasswordAuthenticationToken\",\"authorities\":[\"java.util.Collections$UnmodifiableRandomAccessList\",[{\"@class\":\"org.springframework.security.core.authority.SimpleGrantedAuthority\",\"authority\":\"ROLE_USER\"}]],\"details\":{\"@class\":\"org.springframework.security.web.authentication.WebAuthenticationDetails\",\"remoteAddress\":\"0:0:0:0:0:0:0:1\",\"sessionId\":\"FD624F1AD55A2418CC9815A86AA32696\"},\"authenticated\":true,\"principal\":{\"@class\":\"org.springframework.security.core.userdetails.User\",\"password\":null,\"username\":\"felord\",\"authorities\":[\"java.util.Collections$UnmodifiableSet\",[{\"@class\":\"org.springframework.security.core.authority.SimpleGrantedAuthority\",\"authority\":\"ROLE_USER\"}]],\"accountNonExpired\":true,\"accountNonLocked\":true,\"credentialsNonExpired\":true,\"enabled\":true},\"credentials\":null},\"org.springframework.security.oauth2.server.authorization.OAuth2Authorization.AUTHORIZED_SCOPE\":[\"java.util.Collections$UnmodifiableSet\",[\"message.read\",\"message.write\"]]}",
"state": null,
"authorization_code_value": "EZFxDcsKoaGtyqRTS0oNMg85EcVcyLdVssuD3SV-o0FvNXsSTRjTmCdu0ZPZnVIQ7K4TTSzrvLwBqoRXOigo_dWVNeqE44LjHHL_KtujM_Mxz8hLZgGhtfipvTdpWWR1",
"authorization_code_issued_at": "2021-11-11 18:44:45",
"authorization_code_expires_at": "2021-11-11 18:49:45",
"authorization_code_metadata": "{\"@class\":\"java.util.Collections$UnmodifiableMap\",\"metadata.token.invalidated\":true}",
"access_token_value": "eyJ4NXQjUzI1NiI6IlZGR1F4Q21nSEloX2dhRi13UGIxeEM5b0tBMXc1bGEwRUZtcXFQTXJxbXciLCJraWQiOiJmZWxvcmRjbiIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJmZWxvcmQiLCJhdWQiOiJmZWxvcmQtY2xpZW50IiwibmJmIjoxNjM2NjI3NDg0LCJzY29wZSI6WyJtZXNzYWdlLnJlYWQiLCJtZXNzYWdlLndyaXRlIl0sImlzcyI6Imh0dHA6XC9cL2xvY2FsaG9zdDo5MDAwIiwiZXhwIjoxNjM2NjI3Nzg0LCJpYXQiOjE2MzY2Mjc0ODR9.CFzye9oIh8-ZMpyp9XoIXIQLnj2Sn17yZ9bgn7NYAbrp2hRq-Io_Se2SJpSEMa_Ce44aOGmcLTmIOILYUxlU08QCtHgr4UfCZttzroQhEn3Qui7fixBMprPYqxmu2KL5G_l3q5EWyh4G0ilHpByCBDeBGAl7FpaxSDlelnBfNGs9q6nJCs7aC40U_YPBRLoCBLVK1Y8t8kQvNu8NqCkS5D5DZAogpmlVg7jSIPz1UXVIh7iDTTQ1wJl6rZ1E87E0UroX4eSuYfMQ351y65IUlB14hvKhu03yDLTiVKtujOo3m0DAkJTbk3ZkFZEmDf4N3Yn-ktU7cyswQWa1bKf3og",
"access_token_issued_at": "2021-11-11 18:44:45",
"access_token_expires_at": "2021-11-11 18:49:45",
"access_token_metadata": "{\"@class\":\"java.util.Collections$UnmodifiableMap\",\"metadata.token.claims\":{\"@class\":\"java.util.Collections$UnmodifiableMap\",\"sub\":\"felord\",\"aud\":[\"java.util.Collections$SingletonList\",[\"felord-client\"]],\"nbf\":[\"java.time.Instant\",1636627484.674000000],\"scope\":[\"java.util.Collections$UnmodifiableSet\",[\"message.read\",\"message.write\"]],\"iss\":[\"java.net.URL\",\"http://localhost:9000\"],\"exp\":[\"java.time.Instant\",1636627784.674000000],\"iat\":[\"java.time.Instant\",1636627484.674000000]},\"metadata.token.invalidated\":false}",
"access_token_type": "Bearer",
"access_token_scopes": "message.read,message.write",
"oidc_id_token_value": null,
"oidc_id_token_issued_at": null,
"oidc_id_token_expires_at": null,
"oidc_id_token_metadata": null,
"refresh_token_value": "hbD9dVMpu855FhDDOYapwsQSx8zO9iPX5LUZKeXWzUcbE2rgYRV-sgXl5vGwyByLNljcqVyK9Pgquzbcoe6dkt0_yPQPJfxLY8ezEQ-QREBjxNYqecd6OI9SHMQkBObG",
"refresh_token_issued_at": "2021-11-11 18:44:45",
"refresh_token_expires_at": "2021-11-11 19:44:45",
"refresh_token_metadata": "{\"@class\":\"java.util.Collections$UnmodifiableMap\",\"metadata.token.invalidated\":false}"
}

存储的东西还是比较全的,甚至把Java类都序列化了。

确认授权持久化

资源拥有者(Resource Owner)对授权的确认信息OAuth2AuthorizationConsent的持久化,这个比较简单。下面是定义脚本:

CREATE TABLE oauth2_authorization_consent
(
registered_client_id varchar(100) NOT NULL,
principal_name varchar(200) NOT NULL,
authorities varchar(1000) NOT NULL,
PRIMARY KEY (registered_client_id, principal_name)
);

对应的持久化服务接口为OAuth2AuthorizationConsentService,也要注入Spring IoC:

@Bean
public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
}

持久化到数据库的OAuth2AuthorizationConsent用JSON表示为:

{
"registered_client_id": "658cd010-4d8c-4824-a8c7-a86b642299af",
"principal_name": "felord",
"authorities": "SCOPE_message.read,SCOPE_message.write"
}

JWK全称JSON Web Key,是一个将加密的密钥用JSON对象描述的规范,和JWT一样是JOSE规范的重要组成部分。规范的详细定义可参考JWK文档。JWK参考示例:

{
"keys": [
{
"kty": "RSA",
"x5t#S256": "VFGQxCmgHIh_gaF-wPb1xC9oKA1w5la0EFmqqPMrqmw",
"e": "AQAB",
"kid": "felordcn",
"x5c": [
"MIIDczCCAlugAwIBAgIEURwmYzANBgkqhkiG9w0BAQsFADBqMQ0wCwYDVQQGEwQoY24pMQ0wCwYDVQQIEwQoaG4pMQ0wCwYDVQQHEwQoenopMRMwEQYDVQQKEwooZmVsb3JkY24pMRMwEQYDVQQLEwooZmVsb3JkY24pMREwDwYDVQQDEwgoRmVsb3JkKTAeFw0yMTEwMTgwNDUwMTRaFw0yMjEwMTgwNDUwMTRaMGoxDTALBgNVBAYTBChjbikxDTALBgNVBAgTBChobikxDTALBgNVBAcTBCh6eikxEzARBgNVBAoTCihmZWxvcmRjbikxEzARBgNVBAsTCihmZWxvcmRjbikxETAPBgNVBAMTCChGZWxvcmQpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgo0TPk1td7iROmmLcGbOsZ2F68kTertDwRyk+leqBl+qyJAkjoVgVaCRRQHZmvu/YGp93vOaEd/zFdVj/rFvMXmwBxgYPOeSG0bHkYtFBaUiLf1vhW5lyiPHcGide3uw1p+il3JNiOpcnLCbAKZgzm4qaugeuOD02/M0YcMW2Jqg3SUWpC+9vu9yt5dVc1xpmpwEAamKzvynI3Zxl44ddlA8RRAS6kV0OUcKbEG63G3yZ4MHnhrFrZDuvlwfSSgn0wFOC/b6mJ+bUxByMAXKD0d4DS2B2mVl7RO5AzL4SFcqtZZE3Drtcli67bsENyOQeoTVaKO6gu5PEEFlQ7pHKwIDAQABoyEwHzAdBgNVHQ4EFgQUqbLZ76DtdEEpTZUcgP7LsdGk8/AwDQYJKoZIhvcNAQELBQADggEBAEzfhi6/B00NxSPKAYJea/0MIyHr4X8Ue6Du+xl2q0UFzSkyIiMcPNmOYW5L1PWGjxR5IIiUgxKI5bEvhLkHhkMV+GRQSPKJXeC3szHdoZ3fXDZnuC0I88a1YDsvzuVhyjLL/n5lETRT4QWo5LsAj5v7AX+p46HM0iqSyTptafm2wheEosFA3ro4+vEDRaMrKLY1KdJuvvrrQIRpplhB/JbncmjWrEEvICSLEXm+kdGFgWNXkNxF0PhTLyPu3tEb2xLmjFltALwi3KPUGv9zVjxb7KyYiMnVsOPnwpDLOyREM9j4RLDiwf0tsCqPqltVExvFZouoL26fhcozfcrqC70="
],
"n": "go0TPk1td7iROmmLcGbOsZ2F68kTertDwRyk-leqBl-qyJAkjoVgVaCRRQHZmvu_YGp93vOaEd_zFdVj_rFvMXmwBxgYPOeSG0bHkYtFBaUiLf1vhW5lyiPHcGide3uw1p-il3JNiOpcnLCbAKZgzm4qaugeuOD02_M0YcMW2Jqg3SUWpC-9vu9yt5dVc1xpmpwEAamKzvynI3Zxl44ddlA8RRAS6kV0OUcKbEG63G3yZ4MHnhrFrZDuvlwfSSgn0wFOC_b6mJ-bUxByMAXKD0d4DS2B2mVl7RO5AzL4SFcqtZZE3Drtcli67bsENyOQeoTVaKO6gu5PEEFlQ7pHKw"
}
]
}

JWK的意义在于生成JWT和提供JWK端点给OAuth2.0资源服务器解码校验JWT

JWK会涉及到加密算法,这里使用RSASHA256算法来作为加密算法,并通过Keytool工具来生成.jks公私钥证书文件。当然你也可以通过openssl来生成pkcs12格式的证书。在Spring Security实战干货中已经对生成的方法进行了说明,这里不再赘述。

JWKSource

由于Spring Security的JOSE实现依赖的是nimbus-jose-jwt,所以这里只需要我们实现JWKSource并注入Spring IoC即可。相关代码如下:

    /**
* 加载JWK资源
*
* @return the jwk source
*/
@SneakyThrows
@Bean
public JWKSource<SecurityContext> jwkSource() {
//TODO 这里优化到配置
// 证书的路径
String path = "felordcn.jks";
// 证书别名
String alias = "felordcn";
// keystore 密码
String pass = "123456";

ClassPathResource resource = new ClassPathResource(path);
KeyStore jks = KeyStore.getInstance("jks");
// KeyStore pkcs12 = KeyStore.getInstance("pkcs12");
char[] pin = pass.toCharArray();
jks.load(resource.getInputStream(), pin);
RSAKey rsaKey = RSAKey.load(jks, alias, pin);

JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}

授权服务器元信息配置

客户端信息RegisteredClient包含了Token的配置项TokenSettings和客户端配置项ClientSettings。授权服务器本身也提供了一个配置工具来配置其元信息,大多数我们都使用默认配置即可,唯一需要配置的其实只有授权服务器的地址issuer,在DEMO中虽然我使用localhost:9000issuer没有什么问题,但是在生产中这个地方应该配置为域名

/**
* 配置 OAuth2.0 provider元信息
*
* @return the provider settings
*/
@Bean
public ProviderSettings providerSettings(@Value("${server.port}") Integer port) {
//TODO 生产应该使用域名
return ProviderSettings.builder().issuer("http://localhost:" + port).build();
}

你可以修改本地的hosts文件试试用域名。

到这里Spring Authorization Server的配置就完成了,但是整个授权服务器的配置还没有完成。

授权服务器安全配置

上面是授权服务器本身的配置,授权服务器本身的安全配置是另外一条过滤器链承担的,我们也要对它进行一些配置,都是常规的Spring Security配置,这里给一个简单的配置,也是DEMO中的配置:

@EnableWebSecurity(debug = true)
public class DefaultSecurityConfig {

// @formatter:off
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated()
)
.formLogin();
return http.build();
}
// @formatter:on

/**
* 在内存中抽象一个Spring Security安全用户{@link User},同时该用户也是Resource Owner;
* 实际开发中需要持久化到数据库。
*
* @return the user details service
*/
// @formatter:off
@Bean
UserDetailsService users() {
UserDetails user = User.builder()
.username("felord")
.password("password")
.passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()::encode)
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
// @formatter:on


/**
* 开放一些端点的访问控制。
*
* 如果你使用了一些依赖这些端点的程序,比如Consul健康检查;
* 打开H2数据库web控制台访问控制,方便你查看数据具体看配置文件说明。
*
* @return the web security customizer
*/
@Bean
WebSecurityCustomizer webSecurityCustomizer() {
return web -> web.ignoring().antMatchers("/actuator/health","/h2-console/**");
}
}

到这里一个基于Spring Authorization Server的授权服务器就搭建好了。下一篇我们将实现OAuth2.0的登录功能,敬请期待。

为什么一个项目配置了两个甚至多个SecurityFilterChain?

之所以有两个SecurityFilterChain是因为程序设计要保证职责单一,无论是底层架构还是业务代码,为此HttpSecurity被以基于原型(prototype)的Spring Bean注入Spring IoC。针对本应用中的两条过滤器链,分别是授权服务器的过滤器链和应用安全的过滤器链,它们之间其实互相没有太多联系。

好了,今天的学习就到这里!如果您学习过程中如遇困难?可以加入我们超高质量的Spring技术交流群,参与交流与讨论,更好的学习与进步!更多Spring Boot教程可以点击直达!,欢迎收藏与转发支持!

本文作者:码农小胖哥,
原文链接:https://mp.weixin.qq.com/s/eIYhh1cvGMfA7ancMJ6VeA
版权归作者所有,转载请注明作者、原文、译者等出处信息

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK