此次搭建基于springboot3.0+,所以需要jdk17!!!
节省时间直接用 github 上基于maven已经搭建好的 sample-demo 工程直接运行即可;当然搭建直接copy 工程里面pom和核心配置更快(这部分就是cv大法)
以下先分别介绍下工程的各个模块
sample-demo
demo-authorizationserver 授权服务
messages-resource 资源服务
demo-client 客户端服务
sample-demo 这个工程的存在的作用——就是为了先感觉下 Spring Authorization Server 、Spring Security 做oauth2的认证授权流程。
demo-authorizationserver[授权服务]
核心配置
DefaultSecurityConfig
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class DefaultSecurityConfig {
// 过滤器链
@Bean
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize ->//① 配置鉴权的
authorize
.requestMatchers("/assets/**", "/webjars/**", "/login","/oauth2/**","/oauth2/token").permitAll() //② 忽略鉴权的url
.anyRequest().authenticated()//③ 排除忽略的其他url就需要鉴权了
)
.csrf(AbstractHttpConfigurer::disable)
.formLogin(formLogin ->
formLogin
.loginPage("/login")//④ 授权服务认证页面(可以配置相对和绝对地址,前后端分离的情况下填前端的url)
)
.oauth2Login(oauth2Login ->
oauth2Login
.loginPage("/login")//⑤ oauth2的认证页面(也可配置绝对地址)
.successHandler(authenticationSuccessHandler())//⑥ 登录成功后的处理 可以自定义,如果要开启oauth2认证,一定不要禁用
);
return http.build();
}
//...省略的不是非常关键的代码
}
@EnableWebSecurity:这是一个注解,用于启用Spring Security的功能,也就是配置Spring Security的默认安全配置。
@Configuration(proxyBeanMethods = false):这是一个注解,表示这个类是一个配置类,并且不会代理bean方法。
DefaultSecurityConfig 主要用于配置Spring Security的 默认安全设置,包括允许访问的URL、禁用CSRF保护、配置表单登录和OAuth2登录等。
AuthorizationServerConfig
@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(
HttpSecurity http, RegisteredClientRepository registeredClientRepository,
AuthorizationServerSettings authorizationServerSettings) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
DeviceClientAuthenticationConverter deviceClientAuthenticationConverter =
new DeviceClientAuthenticationConverter(
authorizationServerSettings.getDeviceAuthorizationEndpoint());
DeviceClientAuthenticationProvider deviceClientAuthenticationProvider =
new DeviceClientAuthenticationProvider(registeredClientRepository);
// @formatter:off
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.deviceAuthorizationEndpoint(deviceAuthorizationEndpoint ->
deviceAuthorizationEndpoint.verificationUri("/activate")
)
.deviceVerificationEndpoint(deviceVerificationEndpoint ->
deviceVerificationEndpoint.consentPage(CUSTOM_CONSENT_PAGE_URI)
)
.clientAuthentication(clientAuthentication ->
clientAuthentication
.authenticationConverter(deviceClientAuthenticationConverter)
.authenticationProvider(deviceClientAuthenticationProvider)
)
.authorizationEndpoint(authorizationEndpoint ->
authorizationEndpoint.consentPage(CUSTOM_CONSENT_PAGE_URI))
.oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
// @formatter:on
// @formatter:off
http
.exceptionHandling((exceptions) -> exceptions
.defaultAuthenticationEntryPointFor(
new LoginUrlAuthenticationEntryPoint("/login"),
new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
)
)
.oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer.jwt(Customizer.withDefaults()));
// @formatter:on
return http.build();
}
}
AuthorizationServerConfig 一个Spring Security配置类,用于 配置OAuth2授权服务器的安全设置
DefaultSecurityConfig是默认安全配置,因为是有@EnableWebSecurity 注解,默认安全配置-那可以理解为当前服务的安全配置扩展要在DefaultSecurityConfig里面的SecurityFilterChain过滤链中进行添加。-- 在后面的扩展默认安全配置篇章中可以看到。
AuthorizationServerConfig是配置OAuth2授权服务器的安全设置,-可以理解为当前服务的 OAuth2授权服务的配置 扩展在AuthorizationServerConfig里面的SecurityFilterChain过滤链中进行添加,以上代码中就有体现。
总结
扩展OAuth2授权服务配置的class 要在
AuthorizationServerConfig在SecurityFilterChain构建过程中去添加。
扩展默认登录相关配置的class要在DefaultSecurityConfig在SecurityFilterChain构建过程中去添加。
授权模式演示
授权码模式
/oauth2/token 接口参数说明
| 参数名 | 示例值 | 描述 |
|---|---|---|
| grant_type | authorization_code | 授权类型,固定为 "authorization_code" |
| code | your_code_here | 授权服务器返回的授权码 |
| redirect_uri | your_redirect_uri | 客户端事先注册的回调 URL |
| client_id | your_client_id | 客户端的标识 |
获取授权码[authorization_code]
![]()
浏览器打开URLhttp://127.0.0.1:9000/oauth2/authorize?client_id=messaging-client&response_type=code&scope=message.read&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc
浏览器重定向到 /login 登录页面
![]()
登录
// 内存里初始化了一个用户,直接使用这个用户名和密码进行登录
.username("user1")
.password("password")登录成功后会进入授权页(oauth2/consent)【requireAuthorizationConsent(true)这个地方可以配置:[true 有授权页,false:无授权页] 备注:授权过一次就不会出现了,因为会持久化到数据库,目前demo基于H2重启后数据就没有了 】,进行确认授权 。
![]()
获取授权码-从URL上获取“code=”后面的值
http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc?code=gMXmx2YHmwNFXMjXjgWaveNxspAEUy3q5Jc3m8h-NSjWzYbLAEmraDaUZkFhyBVTrRQlyDMWhePfEUe4Wb2g7sj1Q-mBq9HBTyCwL1qMvRNFEx-UTUtlVNFP7rZnfVWR获取token-POST 请求
/oauth2/token
参数值填充client信息也需要填入
Token 获取成功
jwt解析网址
https://www.box3.cn/tools/jwt.html
一起看看demo
刷新token
请求参数说明
| 参数名 | 示例值 | 描述 |
|---|---|---|
| grant_type | refresh_token | 刷新token,固定为 "refresh_token" |
| refresh_token | hn6N3M6UizLpp8MhyDcjF9qsqUixnzpZtqg_ToPcJWdVbQC4Y5n_wXQZXuchFWKHBLnD1GOBEhdusPxIAizOi7rYz1y-s8ex3bxSI0irgr8zf8QeNXvT6kz8u6ZFE8_ | /oauth2/token返回的refresh_token 值 |
refresh_token 请求URL
http://127.0.0.1:9000/oauth2/token?grant_type=refresh_token&refresh_token=-hn6N3M6UizLpp8MhyDcjF9qsqUixnzpZtqg_ToPcJWdVbQC4Y5n_wXQZXuchFWKHBLnD1GOBEhdusPxIAizOi7rYz1y-s8ex3bxSI0irgr8zf8QeNXvT6kz8u6ZFE8
请求示例![]()
![]()
设备码模式
参数说明
| 参数名 | 示例值 | 描述 |
|---|---|---|
| grant_type | device-messaging-client | 设备码类型,固定为 "device-messaging-client" |
| scope | message.read | 作用域 |
请求URL
http://127.0.0.1:9000/oauth2/device_authorization
{
"user_code": "VRFP-TJHW",
"device_code": "4ZE1FZtPKBki3GVC-YBsnqhxjzQVwSTcBPchE_WKOQL4dg5qw9Z4-4NdTqLPPKknTOKPAy85_ASiKC6Ki-cBNDRLMIil9cK6Dj3HScx1CHvI3qlXDuCzsUk_0sQh-z6b",
"verification_uri_complete": "http://127.0.0.1:9000/activate?user_code=VRFP-TJHW",
"verification_uri": "http://127.0.0.1:9000/activate",
"expires_in": 300
}
verification_uri(验证user_code URL)
http://127.0.0.1:9000/activate填入 user_code
验证 user_code
![]()
也可以直接用 verification_uri_complete 这个value ,从http://127.0.0.1:9000/activate?user_code=VRFP-TJHWURL 上看出是携带了验证码(?user_code=VRFP-TJHW) 参数值。
messages-resource [资源服务]
携带token请求资源服务
资源服务的yml配置授权服务器的 jwt URL
spring: security: oauth2: resourceserver: jwt: issuer-uri: http://127.0.0.1:9000 # 授权服务器(访问资源服务器时会到授权服务器中验>证token)从授权服务器获取到token后,携带token访问资源服务器示例
![]()
用jwt工具看看“access_token”的值解析出来有什么{ "sub": "user1",//用户名 "aud": "messaging-client",//客户端id "nbf": 1693291796, "scope": ["message.read"], "iss": "http://127.0.0.1:9000",//授权服务器 "exp": 1693292096, "iat": 1693291796 }
401异常
有一种情况,携带有效切正确的“access_token”会导致访问资源服务401,并非跨域问题导致,当资源服务器yml配置如下时
spring: security: oauth2: resourceserver: jwt: issuer-uri: http://localhost:9000
![]()
![]()
spring.security.oauth2.resourceserver.jwt.issuer-uri配置值与“access_token”中"iss"值不一致,则会抛出 【 The iss claim is not valid】异常,最终导致401的问题。如何解决这个问题呢?
在授权服务器获取 “access\_token” 时 请求使用的是哪一个域,spring.security.oauth2.resourceserver.jwt.issuer-uri配置值也使用同一个域。
demo-client[客户端服务]
客户端配置 官方说明 URL docs.spring.io/spring-secu…
spring: security: oauth2: client: registration: messaging-client-oidc: # 这个是我们自己定义客户端 provider: spring client-id: messaging-client #客户端id client-secret: secret # 客户端id authorization-grant-type: authorization_code # 授权码模式 redirect-uri: "http://127.0.0.1:8080/login/oauth2/code/{registrationId}" # 这个是一个模板 {baseUrl}/login/oauth2/code/{registrationId} 官方说明:https://docs.spring.io/spring-security/reference/6.1-SNAPSHOT/servlet/oauth2/login/core.html scope: openid, profile client-name: messaging-client-oidc provider: spring: issuer-uri: http://localhost:9000 # 这个对应的是我们自己的授权服务器
运行sample-demo Oauth2 Demo
服务启动顺序
1. demo-authorizationserver[授权服务]
2. messages-resource[资源服务]
3. demo-client[客户端]
说明: messages-resource[资源服务]和demo-client[客户端]可以没有先后顺序;demo-client[客户端]依赖demo-authorizationserver[授权服务]的端点,启动的时候会去请求 demo-authorizationserver 的/.well-known/openid-configuration接口获取配置,如果请求失败时会抛出异常启动失败,所以要先把 demo-authorizationserver 启动。
举例理解【授权服务、资源服务、客户端服务】在微服务架构中担任什么样的一个角色和职责
假如我们有这样的一个微服务:【网关服务】、【认证服务】、【业务服务】
我们访问【业务服务】是通过【网关服务】进行转发的,【网关服务】判断是否登录或者有权限,如果没有登录或者没有权限,就会重定向到【认证服务】的进行登录,登录成功后,【认证服务】把token返回给【网关服务】,【网关服务】会携带token去访问【业务服务】,这样就完成了整个认证和鉴权过程,然后token是存储在【网关服务】的,也没有暴露出来,比较安全demo-client可以把它看作我们的【网关服务】,demo-authorizationserver可以看作我们的【认证服务】,messages-resource可以看作【业务服务】,你这样去理解是否能够理解oauth2的这个流程了呢。