环境:SpringBoot3.1.0 + JDK17
1. 简介
Spring 授权服务器(Spring Authorization Server)是一个提供 OAuth 2.1 和 OpenID Connect 1.0 规范及其他相关规范实现的框架。它构建于 Spring Security 之上,为构建 OpenID Connect 1.0 身份提供程序和 OAuth2 授权服务器产品提供了一个安全、轻量级和可定制的基础。
Spring Authorization Server支持一下功能:

注意:上面的Authorization Grant一栏,在OAuth2.1中已经删除了下面2个模式
密码模式(password)
简化模式(implicit)
2. 环境要求
本文使用的Spring Authorization Server版本是1.1.0,所以对JDK的版本要求是17以上。
Spring Authorization Server构建在Spring Security,所以在学习之前最后是对Spring Security有一定的了解,尤其是从Spring Security5.7开始自定义安全配置由之前的WebSecurityConfigurerAdapter替换为SecurityFilterChain。
3. 开发第一个程序
3.1 引入依赖
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-oauth2-authorization-server</artifactId><version>1.1.4</version></dependency>
3.2 自定义配置类
在必要的情况下,我们是可以自定义@Bean配置,下面依次介绍每一种自定义Bean的作用。
协议端点的Spring安全过滤器链
简单说就是OAuth2.1与OIDC相关的接口。
(1)public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);// 启用OpenID Connect 1.0http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults());http// 当未从授权端点进行身份验证时,重定向到登录页面.exceptionHandling((exceptions) -> exceptions.defaultAuthenticationEntryPointFor(new LoginUrlAuthenticationEntryPoint("/login"), new MediaTypeRequestMatcher(MediaType.TEXT_HTML)))// 接受用户信息 and/or 客户端注册的访问令牌.oauth2ResourceServer((resourceServer) -> resourceServer.jwt(Customizer.withDefaults()));return http.build();}
用于身份验证的过滤器链
该bean的作用就是在老版本中配置的WebSecurityConfigurerAdapter一样进行用户登录安全验证。
(2)public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())// 表单登录处理从授权服务器过滤链重定向到登录页面的操作.formLogin(Customizer.withDefaults());return http.build();}
用户加载服务接口
该接口熟悉Spring Security的都非常清楚,只有一个方法通过用户名加载用户,而在内部是由DaoAuthenticationProvider来调用的。这里使用内存用户。
public UserDetailsService userDetailsService() {UserDetails userDetails = User.withUsername("user").password("{noop}123123").authorities(Arrays.asList(new SimpleGrantedAuthority("p1"), new SimpleGrantedAuthority("p3"))).roles("USER").build();UserDetails adminUserDetails = User.withUsername("admin").password("{noop}123123").authorities(Arrays.asList(new SimpleGrantedAuthority("p1"), new SimpleGrantedAuthority("p2"), new SimpleGrantedAuthority("p3"))).roles("MANAGER").build();return new InMemoryUserDetailsManager(userDetails, adminUserDetails);}
管理客户端
为每一种客户端提供配置信息;系统默认提供了基于内存的,基于JDBC的。
@Beanpublic RegisteredClientRepository registeredClientRepository() {RegisteredClient oidcClient = RegisteredClient.withId("pack001")// 客户端id.clientId("123123")// 客户端密钥.clientSecret("{noop}666666")// 客户端支持的方法.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC).clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)// 支持的授权类型.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN).authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)// 授权成功后跳转页面.redirectUri("http://localhost:8881/index.html").clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();return new InMemoryRegisteredClientRepository(oidcClient);}
签名访问令牌
JWKSource是一个接口,它定义了从JWK(JSON Web Key)集合中获取JWK的方式。JWK是一种表示加密密钥的标准格式,常用于 JWT 签名和加密。JWKSource<SecurityContext> 中的泛型参数SecurityContext通常是一个上下文对象,包含了与安全相关的信息,例如用户身份、角色等。这个类通常用于在处理JWT时提供密钥的来源。例如,当需要验证或生成JWT时,可以使用这个类来获取相应的密钥。
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);}// 生成RSA密钥对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;}
解码已签名的访问令牌
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);}
配置Spring Authorization Server
在这里你可以配置每个Endpoint的URI等相关信息。
public AuthorizationServerSettings authorizationServerSettings() {return AuthorizationServerSettings.builder()// 授权Endpoint.authorizationEndpoint("/oauth2/v1/authorize")// 如果开启了OpenId connect 1.0 并且authorize接口请求的scope中包含openid,那么// 在请求token接口的时候会通过OAuth2AuthorizationCodeAuthenticationProvider进行生成id_token// 并且你的token生成必须是jwt类型.tokenEndpoint("/oauth2/v1/token")// 自省token的Endpoint.tokenIntrospectionEndpoint("/oauth2/v1/introspect")// 撤销token的Endpoint.tokenRevocationEndpoint("/oauth2/v1/revoke").jwkSetEndpoint("/oauth2/v1/jwks")// OIDC用户信息Endpoint.oidcUserInfoEndpoint("/userinfo").build();}
授权同意服务
如果不提供该授权同意服务,那么当你认证授权成功以后再跳转到 redirect_uri时,将会出错(能跳转但是没有code值)。默认还提供有基于Jdbc实现的。
public OAuth2AuthorizationConsentService auth2AuthorizationConsentService() {InMemoryOAuth2AuthorizationConsentService consentService = new InMemoryOAuth2AuthorizationConsentService() ;OAuth2AuthorizationConsent ac = OAuth2AuthorizationConsent// 这里的pack001要和上面配置的RegisteredClientRepository id一致// 用户名也要对应上.withId("pack001", "user")// 这里的权限随意.authority(new SimpleGrantedAuthority("xxoo")).build() ;consentService.save(ac) ;return consentService ;}
有了上面的配置后,接下来就可以启动服务验证认证码模式及获取token了。
4. 测试认证服务
通过浏览器访问
/oauth2/v1/authorize?client_id=123123&response_type=code&redirect_uri=http://localhost:8080/index.html 授权接口
会跳转到登录页面

输入,上面配置的任意用户即可。成功后会调整到redirect_uri的页面

地址中已经附带了code参数值,接下来通过code值获取token。
访问如下获取token接口
/oauth2/v1/token?grant_type=authorization_code&code=输入刚刚获取到的code&redirect_uri=http://localhost:8080/index.html
获取token接口

成功获取token
整体Spring Authorization Server使用还是比较简单的,核心的类在上面,也做了介绍。下篇将会介绍更多的关于自定义实现的功能。
完毕!!!



