目录 前言本文开发环境介绍开发环境端口说明认证授权服务pom.xml依赖新建Oauth2ServerAutoConfiguration类main函数yml配置 第三方应用OAuth客户端pom.xml依赖新建Oauth2Clie
Spring Boot 3已经发布一段时间,网上关于Spring Boot 3的资料不是很多,本着对新技术的热情,学习和研究了大量Spring Boot 3新功能和新特性,感兴趣的同学可以参考Spring官方资料全面详细的新功能/新改进介绍
新特性有很多,本文主要针对OAuth 2.0的集成,如果快速开发自己的认证授权服务、OAuth客户端以及资源服务
开发依赖 | 版本 |
---|---|
Spring Boot | 3.0.2 |
新建三个服务,分别对应认证授权服务、OAuth客户端以及资源服务
服务 | 端口 |
---|---|
认证授权服务 | 8080 |
OAuth客户端服务 | 8081 |
资源服务 | 8082 |
Spring发布了spring-security-oauth2-authorization-server项目,目前最新版是1.0版,pom.xml依赖如下
<dependencies> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-WEBartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-securityartifactId> dependency> <dependency> <groupId>org.springframework.securitygroupId> <artifactId>spring-security-oauth2-authorization-serverartifactId> <version>${spring-security-oauth2-authorization-server.version}version> dependency>dependencies>
package com.wen3.oauth.ss.authserver.authconfigure;import com.nimbusds.jose.jwk.JWKSet;import com.nimbusds.jose.jwk.RSAKey;import com.nimbusds.jose.jwk.source.ImmutableJWKSet;import com.nimbusds.jose.jwk.source.JWKSource;import com.nimbusds.jose.proc.SecurityContext;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.annotation.Order;import org.springframework.security.config.Customizer;import org.springframework.security.config.annotation.web.builders.httpsecurity;import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;import org.springframework.security.core.userdetails.User;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.oauth2.core.AuthorizationGrantType;import org.springframework.security.oauth2.core.ClientAuthenticationMethod;import org.springframework.security.oauth2.core.oidc.OidcScopes;import org.springframework.security.oauth2.Jwt.JwtDecoder;import org.springframework.security.oauth2.server.authorization.client.InMemoryReGISteredClientRepository;import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;import org.springframework.security.provisioning.InMemoryUserDetailsManager;import org.springframework.security.web.SecurityFilterChain;import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import java.security.KeyPair;import java.security.KeyPairGenerator;import java.security.interfaces.RSAPrivateKey;import java.security.interfaces.RSAPublicKey;import java.util.UUID;@Configurationpublic class Oauth2ServerAutoConfiguration { @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(OAuth2ResourceServerConfigurer::jwt); return http.build(); } @Bean @Order(2) public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests((authorize) -> authorize .requestMatchers(new AntPathRequestMatcher("/actuator*.JSON"), new AntPathRequestMatcher("*.html")).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("test") .password("test") .roles("USER") .build(); return new InMemoryUserDetailsManager(userDetails); } @Bean public RegisteredClientRepository registeredClientRepository() { RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString()) .clientId("demo-client-id") .clientSecret("{noop}demo-client-secret") .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)// .tokenSettings(TokenSettings.builder().accessTokenFormat(OAuth2TokenFormat.REFERENCE).build()) .redirectUri("http://127.0.0.1:8081/login/oauth2/code/client-id-1") .redirectUri("http://127.0.0.1:8081/login/oauth2/code/client-id-2") .scope(OidcScopes.OPENID) .scope(OidcScopes.PROFILE) .scope("message.read") .scope("message.write") .scope("user_info") .scope("pull_requests") // 登录成功后对scope进行确认授权 .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) .build(); return new InMemoryRegisteredClientRepository(registeredClient); } @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(); }}
@SpringBootApplicationpublic class OauthServerApplication { public static void main(String[] args) { SpringApplication.run(OauthServerApplication.class, args); }}
server: port: 8080
<dependencies> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-webartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-securityartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-oauth2-clientartifactId> dependency>dependencies>
package com.wen3.oauth.ss.authclient.autoconfigure;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.http.SessionCreationPolicy;import org.springframework.security.web.SecurityFilterChain;@Configurationpublic class Oauth2ClientAutoConfiguration { @Bean public SecurityFilterChain authorizationClientSecurityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests() .anyRequest().authenticated() .and().loGout() .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS) .and().oauth2Client() .and().oauth2Login(); return http.build(); }}
package com.wen3.oauth.ss.authclient.controller;import lombok.extern.slf4j.Slf4j;import org.springframework.security.core.Authentication;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@Slf4j@RestControllerpublic class OauthClientDemoController { @RequestMapping(path = "/hello") public String demo() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); log.info("authentication: {}", authentication); return "hello"; }}
@SpringBootApplicationpublic class OauthServerApplication { public static void main(String[] args) { SpringApplication.run(OauthServerApplication.class, args); }}
server: port: 8081 servlet: session: cookie: # 需要更换存放sessionId的cookie名字,否则认证服务和客户端的sessionId会相互覆盖 name: jsESSIONID-2spring: security: oauth2: client: registration: client-id-1: provider: demo-client-id client-id: demo-client-id client-secret: demo-client-secret authorization-grant-type: authorization_code redirect-uri: '{baseUrl}/{action}/oauth2/code/{registrationId}' # client-authentication-method: POST scope: user_info, pull_requests client-name: demo-client-id client-id-2: provider: demo-client-id2 client-id: demo-client-id client-secret: demo-client-secret authorization-grant-type: authorization_code redirect-uri: '{baseUrl}/{action}/oauth2/code/{registrationId}' # client-authentication-method: POST scope: user_info, pull_requests client-name: demo-client-id2 provider: demo-client-id: authorization-uri: http://127.0.0.1:8080/oauth2/authorize token-uri: http://127.0.0.1:8080/oauth2/token user-info-uri: http://127.0.0.1:8082/user/info user-name-attribute: name jwk-set-uri: http://127.0.0.1:8080/oauth2/jwks demo-client-id2: authorization-uri: http://127.0.0.1:8080/oauth2/authorize token-uri: http://127.0.0.1:8080/oauth2/token user-info-uri: http://127.0.0.1:8082/user/info user-name-attribute: name jwk-set-uri: http://127.0.0.1:8080/oauth2/jwks
<dependencies> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-webartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-securityartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-oauth2-resource-serverartifactId> dependency>dependencies>
package com.wen3.oauth.ss.resourceserver.autoconfigure;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.web.SecurityFilterChain;@Configurationpublic class ResourceServerAutoConfiguration { @Bean public SecurityFilterChain resourceServerSecurityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests().anyRequest().authenticated().and() .oauth2ResourceServer().jwt(); return http.build(); }}
package com.wen3.oauth.ss.resourceserver.controller;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import org.springframework.http.MediaType;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;import java.util.Map;@RestControllerpublic class UserController { @RequestMapping(path = "/user/info", method = {RequestMethod.GET,RequestMethod.POST}, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public Map<String, Object> getUser(HttpServletRequest request, HttpServletResponse response) { Map<String, Object> map = new HashMap<>(); map.put("name", "xxx"); return map; }}
package com.wen3.oauth.ss.resourceserver;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class ResourceServerApplication { public static void main(String[] args) { SpringApplication.run(ResourceServerApplication.class, args); }}
server: port: 8082spring: security: oauth2: resourceserver: jwt: jwk-set-uri: http://127.0.0.1:8080/oauth2/jwks
http://127.0.0.1:8081/hello
因为配置了多个client,会让用户选择用哪个client进行OAuth登录
以上所有页面都是Spring默认的,真实业务开发会自定义这些页面
http://127.0.0.1:8080/.well-known/openid-configuration
{"issuer":"http://127.0.0.1:8080","authorization_endpoint":"http://127.0.0.1:8080/oauth2/authorize","token_endpoint":"http://127.0.0.1:8080/oauth2/token","token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt"],"jwks_uri":"http://127.0.0.1:8080/oauth2/jwks","userinfo_endpoint":"http://127.0.0.1:8080/userinfo","response_types_supported":["code"],"grant_types_supported":["authorization_code","client_credentials","refresh_token"],"revocation_endpoint":"http://127.0.0.1:8080/oauth2/revoke","revocation_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt"],"introspection_endpoint":"http://127.0.0.1:8080/oauth2/introspect","introspection_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"scopes_supported":["openid"]}
issuer-uri
,启动的时候会调用${issuer-uri}/.well-known/openid-configuration
获取provider
配置信息,如果issuer-uri
配置了path
也会替换成/.well-known/openid-configuration
/.well-known/openid-configuration
这个接口获取到的user-info-uri
地址是http://127.0.0.1:8080/userinfo
所以会从授权服务获取用户信息/userinfo
接口正常返回,则需要在配置registration
时,在scope增加openid
,同时scope还需要在profile
、email
、address
、phone
中增加至少一个,修改后的yml配置如下server: port: 8081 servlet: session: cookie: # 需要更换存放sessionId的cookie名字,否则认证服务和客户端的sessionId会相互覆盖 name: JSESSIONID-2spring: security: oauth2: client: registration: client-id-1: provider: demo-client-id client-id: demo-client-id client-secret: demo-client-secret authorization-grant-type: authorization_code redirect-uri: '{baseUrl}/{action}/oauth2/code/{registrationId}'# client-authentication-method: POST scope: openid, profile, user_info, pull_requests client-name: demo-client-id client-id-2: provider: demo-client-id2 client-id: demo-client-id client-secret: demo-client-secret authorization-grant-type: authorization_code redirect-uri: '{baseUrl}/{action}/oauth2/code/{registrationId}'# client-authentication-method: POST scope: openid, profile, user_info, pull_requests client-name: demo-client-id2 provider: demo-client-id: issuer-uri: http://127.0.0.1:8080 demo-client-id2: issuer-uri: http://127.0.0.1:8080
本人经过研读SpringBoot3相关源码,基本上把所有功能都体验了一遍,这篇文章主要是针对最新版的SpringBoot集成OAuth功能进行演示,背后的原理,大家有疑问的可以留言交流。
来源地址:https://blog.csdn.net/friendlytkyj/article/details/128889875
--结束END--
本文标题: Spring Boot 最新版3.x 集成 OAuth 2.0实现认证授权服务、第三方应用客户端以及资源服务
本文链接: https://www.lsjlt.com/news/378596.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-04-03
2024-04-03
2024-04-01
2024-01-21
2024-01-21
2024-01-21
2024-01-21
2023-12-23
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0