介绍
OAuth 1.0 组织已经开始开发基于对OAuth的支持,该框架就是Spring Security OAuth。其实现了大部分的OAuth规范,并提供了资源服务器、客户端和授权服务器。
2018年1月,Spring 官方发布了一个将会停更Spring Security OAuth的通知,并开始在 Spring Security 5.0中构建下一代0Auth2.0支持
2019年11月,Spring Security 0Auth中客户端、资源服务器的功能大部分已迁移到Spring Security 5>中,在5.3版本中完成了迁移工作,并添加了许多新功能,比如对OpenID Connect 1.0的支持。同时还宣布不再支持授权服务器,因为Spring觉得授权服务器更像是一个产品,而Spring Security作为框架,并不适合做这件事情,而且已经有大量商业和开源并且成熟的授权服务器。
2022年5月31日,Spring Security OAuth正式归档。
** Spring Authorization Server**
`Spring Security OAuth的停止维护,以及Spring Security不再提供授权服务器这件事,在社区一石激起千层浪,引起很多人的反对,经过Spring社区的努力,Spring决定在2020年4月开始启动新的授权服务器项目。
Spring Authorization Server是一个授权服务器框架,提供 OAuth 2.1 和 OpenID Connect 1.0 规范及其他相关规范的实现。它建立在 Spring Security 之上,为构建开发标准的授权服务器产品提供了一个安全、轻量级和可定制的基础。
Spring Authorization Server
功能特性
授权模式支持:
- 授权码模式
- 客户端模式
- 刷新令牌模式
令牌格式:
- JWT
- JWS
客户端认证方式支持
- client_secret_basic:基于Basic消息头认证
header里面的Authorization值为 Basic base64(clientId:clientSecret)
- client_secret_post:POST请求进行认证
直接将clientId、clientSecret、grant_type放入表单元素中
- private_key_jwt: 基于JWT 进行认证,请求方使用私钥对JWT签名,授权服务器使用对应公钥进行验签认证
- client_secret_jwt:基于JWT 进行认证,对JWT使用客户端密码+签名算法 签名
- none (public clients):公共客户端
协议端点支持:
- OAuth2 Authorization Endpoint:申请授权端点,默认为/oauth2/authorize
- OAuth2 Token Endpoint:获取访问令牌端点,默认为/oauth2/token
- OAuth2 Token Introspection Endpoint:令牌自省端点,默认为/oauth2/introspect
- OAuth2 Token Revocation Endpoint:令牌撤销端点,默认为/oauth2/revoke
- OAuth2 Authorization Server Metadata Endpoint:获取授权服务器元信息的端点,默认为/.well-known/oauth-authorization-server
- JWK Set Endpoint:JWK信息端点,默认为/oauth2/jwks
- OpenID Connect 1.0 Provider Configuration Endpoint:查询提供者配置端点,默认为/.well-known/openid-configuration
- OpenID Connect 1.0 UserInfo Endpoint:用户信息端点,默认为/userinfo
- OpenID Connect 1.0 Client Registration Endpoint:客户端注册端点,默认为/connect/register
环境搭建
依赖
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
</dependency>
数据库存储client需要使用jdbc
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
数据库新建
配置类:
@Configuration
public class SecurityConfig {
@Autowired
private JdbcTemplate jdbcTemplate;
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
http
// Redirect to the login page when not authenticated from the
// authorization endpoint
.exceptionHandling((exceptions) -> exceptions
.authenticationEntryPoint(
new LoginUrlAuthenticationEntryPoint("/login"))
);
// Accept access tokens for User Info and/or Client Registration
// .oauth2ResourceServer();
return http.build();
}
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
throws Exception {
http
.authorizeHttpRequests(
(authorize) -> authorize
.requestMatchers("/test/**").permitAll()
.anyRequest().authenticated()
)
// Form login handles the redirect to the login page from the
// authorization server filter chain
.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(userDetails);
}
@Bean
public RegisteredClientRepository registeredClientRepository() {
return new JdbcRegisteredClientRepository(jdbcTemplate);
}
@Bean
public JWKSource<SecurityContext> jwkSource() {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
JWKSet jwkSet = new JWKSet(rsaKey);
return new ImmutableJWKSet<>(jwkSet);
}
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().build();
}
}
获取token
客户端模式
url:http://localhost:8080/oauth2/token
Spring Resource Server
准备
最小依赖:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
配置:
@Configuration
@EnableWebFluxSecurity
public class ResourceConfig {
@Bean
public SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) {
return http
// 统一资源服务器JWT Token校验
.oauth2ResourceServer(oauth2 ->
oauth2
.jwt(Customizer.withDefaults())
)
.csrf(ServerHttpSecurity.CsrfSpec::disable)
// 通过认证后的处理器
.build();
}
}
yml文件
spring:
security:
oauth2:
resourceserver:
jwt:
#需要把认证服务器的jwk地址写上
jwk-set-uri: http://localhost:8080/oauth2/jwks
token获取以及流程:
使用
直接将从认证服务器中拿到的token,来访问资源服务器即可