这篇文章给大家聊聊关于Spring OAuth2 授权服务器配置详解-spring security oauth2,以及对应的知识点,希望对各位有所帮助,不要忘了收藏本站哦。
前两篇文章分别体验了Spring Authorization Server的使用并讲解了其各种过滤器的功能。今天我们就来说说Spring Authorization Server授权服务器的配置。强烈建议您尝试自己手动构建。写在纸上就会很容易理解,做详细了你就知道了。增加你的代码量是提高你的编程技能的唯一途径,这就是本教程的意义。
配置依赖
首先,创建一个Spring Boot Servlet Web 项目。这并不难,我就不详细说了。集成Spring Authorization Server需要引入:
!--springsecuritystarter 必须--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependencydependencygroupIdorg.springframework.security/groupIdartifactIdspring-security-oauth2-authorization-server/artifactId!--截至目前的版本--version0 。 2.0/version/dependencyOAuth2.0 客户端客户端需要向授权服务器注册并持久化。 Spring Authorization Server 提供了JDBC 实现,请参阅JdbcRegisteredClientRepository。为了演示方便,我使用H2数据库,需要以下依赖:
!--必须引入jdbc否则会自己实现--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-jdbc/artifactId/dependencydependencygroupIdcom.h2database/groupIdartifactIdh2/artifactId/dependency制作可以切换到其他关系型数据库,数据库脚本在Spring授权服务器入门一文的DEMO中。
Spring Authorization Server配置
接下来是Spring Authorization Server的配置。
过滤器链配置根据上一篇文章过滤器链的拆解,我们需要向Spring Security过滤器链中注入一些特定的过滤器。这些过滤器的配置是由OAuth2AuthorizationServerConfigurer完成的。以下是默认配置:
voiddefaultOAuth2AuthorizationServerConfigurer(HttpSecurityhttp)throwsException{OAuth2AuthorizationServerConfigurerHttpSecurityauthorizationServerConfigurer=newOAuth2AuthorizationServerConfigurer();//TODO 您可以根据自己的需求对authorizationServerConfigurer进行一些个性化的配置。 RequestMatcherauthorizationServerEndpointsMatcher=authorizationServerConfigurer.getEndpointsM atcher();//拦截授权服务器相关的请求端点http.requestMatcher(authorizationServerEndpointsMatcher). authorizeRequests().anyRequest().authenticated().and()//忽略相关端点的csrf.csrf(csrf-csrf.ignoringRequestMatchers(authorizationServerEndpointsMatcher))//启用表单login.formLogin().and()//apply 授权服务器配置.apply(authorizationServerConfigurer);} 可以调用OAuth2AuthorizationServerConfigurer提供的配置方法来进行一些个性化的配置。
OAuth2.0客户端信息持久化
此信息将保存到数据库中。 Spring Authorization Server 提供了三个DDL 脚本。在入门教程的DEMO中,H2会自动初始化并执行这些DDL脚本。如果切换到Mysql等数据库,可能需要自己执行。
客户端配置信息注册授权服务器要求客户端必须注册,防止非法客户端发起授权申请。就像你平时去一些开放平台申请ClientID和Secret一样。下面是定义脚本:
CREATETABLEoauth2_registered_client(idvarchar(100)NOTNULL,client_idvarchar(100)NOTNULL,client_id_issued_attimestampDEFAULTCURRENT_TIMESTAMPNOTNULL,client_secretvarchar(200)NULL,client_secret_expires_attimestampNULL,client_namevarchar(200)NOTNULL,client_au thentication_methodsvarchar(10 00)NOTNULL,authorization_grant_typesvarchar(1000)NOTNULL,redirect_urisvarchar(1000)NULL,scopesvarchar (1000) NOTNULL, client_settingsvarchar (2000) NOTNULL, token_settingsvarchar (2000) NOTNULL, PRIMARYKEY (id));对应的Java类是RegisteredClient:
publicclassRegisteredClientimplementsSerializable{privatestaticfinallongserialVersionUID=Version.SERIAL_VERSION_UID;privateStringid;privateStringclientId;privateInstantclientIdIssuedAt;privateStringclientSecret;privateInstantclientSecretExpiresAt;privateStringclientName;privateSetClientAuthenticationMethodclientAuthent icationMethods;privateSetAuthorizationGrantTypeauthorizationGrantTypes;privateSetStringredirectUris;privateSetString scopes;privateClientSettingsclientSettings;privateTokenSettingstokenSettings;//省略} 定义客户端可以通过以下Builder方法实现:
RegisteredClientregisteredClient=RegisteredClient.withId(UUID.randomUUID().toString())//唯一的客户端ID和密码.clientId('felord-client').clientSecret('secret')//名称可以是undefined.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:808 0/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-1118:01:09','client_secret'333 60'{bcrypt }$2a $10 $XKZ8iUckDcdQWnqw682zV.DVyGuov8Sywx1KyAn4tySsw.Jtltg0。','client_secret_expires_at':null,'client_name':'felord','client_authentication_methods':'client_secret_basic','authorization_ grant_types'333 60'refresh_token,client_credentials,authorization_code','redirect_uris':'http://127.0 .0.1:8080 /foo/bar,http://127.0.0.1:8080/授权,http://127.0.0.1:8080/login/oauth2/code/felord-oidc,https://baidu.com','scopes':'openid,message.read,message.write', '客户端设置': '{\'@class\':\'java.util.Collections$UnmodifyingMap\',\'settings.client.require-proof-key\':false,\'settings.client.require-authorization-consent\ ':true }','token_settings':'{\'@class\':\'java.util.Collections$UnmodifyingMap\',\'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 客户端应用程序的配置密切相关。
@BeanpublicRegisteredClientRepositoryregisteredClientRepository(JdbcTemplatejdbcTemplate){returnnewJdbcRegisteredClientRepository(jdbcTemplate);} 不要忘记调用save(RegisteredClient)方法来持久化需要注册的客户端信息。
该实现依赖于spring-boot-starter-jdbc 类库。您也可以在闲暇时使用Mybatis 来实现。
OAuth2授权信息持久化记录了授权资源所有者对某个客户端的某条授权记录。对应的Java类是OAuth2Authorization。下面是定义脚本:
CREATETABLEoauth2_authorization(idvarchar(100)NOTNULL,registered_client_idvarchar(100)NOTNULL,principal_namevarchar(200)NOTNULL,authorization_grant_typevarchar(100)NOTNULL,attributesvarchar(4000)NULL,statevarchar(500)NULL,authorization_code_valueblobNULL,`authorization_code_issued_ at`timestampNULL ,authorization_code_expires_attimestampNULL,authorization_code_metadatavarchar( 2000 )NULL,access_token_valueblobNULL,access_token_issued_attimestampNULL,access_token_expires_attimestampNULL,access_token_metadatavarchar(2000)NULL,access_token_typevarchar(100)NULL,access_token_scopesvarchar(1000)NULL,oidc_id_token_valueblobNULL,oidc_id_token_issued_attime stampNULL,oidc_id_token_expires_attimestampNULL,oidc_id_token_metadatavarchar(2000)NULL,refresh_token_valueblobNULL,refresh_token_issued_attimestampNULL,refresh_token_expires_attimestampNULL,refresh_token_metadatavarchar(2000 )NULL ,PRIMARYKEY(id));这里的机制还没研究过,先挖坑吧。
它还需要一个持久化服务接口OAuth2AuthorizationService并注入Spring IoC:
/***管理OAuth2授权信息服务**@paramjdbcTemplatethejdbctemplate*@paramregisteredClientRepositorytheregisteredclientrepository*@returntheoauth2authorizationservice*/@BeanpublicOAuth2AuthorizationServiceauthorizationService(JdbcTemplatejdbcTemplate,RegisteredClientRepositoryregisteredClientRepository){returnnewJdbcOAuth2Auth orizationService(jdbcTemplate,registeredClientRepository);} 2持久化到数据库的授权用JSON表示为:
{'id':'aa2f6e7d-d9b9-4360-91ef-118cbb6d4b09','registered_client_id':'658 cd010-4d8c-4824-a8c7-a86b642299af','principal_name':'felord','authorization_g rant_type':'authorization_code','属性':'{\'@class\':\'java.util.Collections$UnmodifyingMap\',\'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\':{\'值
e\":\"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" } JWKJWK全称JSON Web Key,是一个将加密的密钥用JSON对象描述的规范,和JWT一样是JOSE规范的重要组成部分。规范的详细定义可参考JWK文档。JWK参考示例:授权服务器元信息配置
客户端信息RegisteredClient包含了Token的配置项TokenSettings和客户端配置项ClientSettings。授权服务器本身也提供了一个配置工具来配置其元信息,大多数我们都使用默认配置即可,唯一需要配置的其实只有授权服务器的地址issuer,在DEMO中虽然我使用localhost:9000了issuer没有什么问题,但是在生产中这个地方应该配置为域名。 /** * 配置 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 OAuth2 授权服务器配置详解-spring security oauth2的内容到此结束,希望对大家有所帮助。
本文采摘于网络,不代表本站立场,转载联系作者并注明出处:https://www.iotsj.com//kuaixun/8037.html
用户评论
终于看懂了OAuth2授权服务器的配置!感谢作者讲解这么详细。
有6位网友表示赞同!
想搞明白Spring Security OAuth2,这篇文章简直太棒啦!
有9位网友表示赞同!
最近在学习Spring Boot,OAuth2是重点,这篇博客正好填补了我知识的空白。
有10位网友表示赞同!
配置授权服务器总是让我头疼,看了这篇文章后感觉不再那么难了。
有8位网友表示赞同!
详细介绍 OAuth2 协议的各个步骤和 Spring Security 的集成方式,受益匪浅!
有14位网友表示赞同!
代码示例清晰易懂,相信可以很快上手配置OAuth2认证服务器。
有8位网友表示赞同!
喜欢作者的讲解风格,通俗易懂,让人轻松理解复杂的概念。
有8位网友表示赞同!
学习 OAuth2 授权服务器真是太兴奋了!
有13位网友表示赞同!
这篇博文让我对 Spring OAuth2 的实现有了更深入的了解。
有9位网友表示赞同!
收藏了,以后需要时再来看!
有8位网友表示赞同!
终于能自己搭建一个OAuth2授权服务器了!感谢作者分享经验。
有12位网友表示赞同!
希望作者后续还能分享一些其他关于OAuth2的知识。
有20位网友表示赞同!
感觉这篇文章把OAuth2配置的过程讲得很清晰,入门难度大大降低。
有8位网友表示赞同!
学习OAuth2 的小伙伴们一定要看看这篇博客!
有18位网友表示赞同!
Spring Security OAuth2 真的是强大的功能,需要好好研究一下。
有9位网友表示赞同!
文章很全面,涵盖了OAuth2配置的大部分内容。
有20位网友表示赞同!
作者的经验分享非常宝贵,能帮助很多开发者解决实际问题。
有18位网友表示赞同!
这个配置教程确实值得参考!
有19位网友表示赞同!