spring authorization server授权服务器教程,集成jdbc,使用版本0.2.2

编程入门 行业动态 更新时间:2024-10-23 11:19:31

spring authorization server授权服务器教程,集成jdbc,使用<a href=https://www.elefans.com/category/jswz/34/1771446.html style=版本0.2.2"/>

spring authorization server授权服务器教程,集成jdbc,使用版本0.2.2

文章目录

  • 前言
  • 一、创建授权服务器
    • 1.引入库
    • 2.创建相关数据表
    • 3.配置文件
    • 4.放入官方认证html页面
    • 5.生成jks文件
    • 6.配置KeyPair
    • 7.配置AuthorizationServerConfig授权服务器配置
    • 8.配置WebSecurityConfig基础security配置
  • 9.配置AuthorizationConsentController
  • 三 启动项目,这里我们测试授权码模式
  • 总结


前言

spring authorization server是spring团队最新的认证授权服务器,之前的oauth2后面会逐步弃用。不过目前项目还没有到可生产阶段。
springsecurityoauth迁移到新的授权服务器指南 .0-Migration-Guide
spring authorization server官方demo
本文基于官方demo修改

一、创建授权服务器

创建springboot启动项目,版本2.6.3

1.引入库

代码如下(示例):

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- Spring Security OAuth2 依赖 --><!-- Spring Authorization Server--><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-oauth2-authorization-server</artifactId><version>0.2.2</version></dependency><!-- 新版 Resource Server 类库 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-oauth2-resource-server</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-rsa</artifactId><version>1.0.10.RELEASE</version></dependency>

2.创建相关数据表

sql示例:

CREATE TABLE oauth2_registered_client (id varchar(100) NOT NULL,client_id varchar(100) NOT NULL,client_id_issued_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,client_secret varchar(200) DEFAULT NULL,client_secret_expires_at timestamp DEFAULT NULL,client_name varchar(200) NOT NULL,client_authentication_methods varchar(1000) NOT NULL,authorization_grant_types varchar(1000) NOT NULL,redirect_uris varchar(1000) DEFAULT NULL,scopes varchar(1000) NOT NULL,client_settings varchar(2000) NOT NULL,token_settings varchar(2000) NOT NULL,PRIMARY KEY (id)
);
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)
);
/*
IMPORTANT:If using PostgreSQL, update ALL columns defined with 'blob' to 'text',as PostgreSQL does not support the 'blob' data type.
*/
CREATE TABLE oauth2_authorization (id varchar(100) NOT NULL,registered_client_id varchar(100) NOT NULL,principal_name varchar(200) NOT NULL,authorization_grant_type varchar(100) NOT NULL,attributes blob DEFAULT NULL,state varchar(500) DEFAULT NULL,authorization_code_value blob DEFAULT NULL,authorization_code_issued_at timestamp DEFAULT NULL,authorization_code_expires_at timestamp DEFAULT NULL,authorization_code_metadata blob DEFAULT NULL,access_token_value blob DEFAULT NULL,access_token_issued_at timestamp DEFAULT NULL,access_token_expires_at timestamp DEFAULT NULL,access_token_metadata blob DEFAULT NULL,access_token_type varchar(100) DEFAULT NULL,access_token_scopes varchar(1000) DEFAULT NULL,oidc_id_token_value blob DEFAULT NULL,oidc_id_token_issued_at timestamp DEFAULT NULL,oidc_id_token_expires_at timestamp DEFAULT NULL,oidc_id_token_metadata blob DEFAULT NULL,refresh_token_value blob DEFAULT NULL,refresh_token_issued_at timestamp DEFAULT NULL,refresh_token_expires_at timestamp DEFAULT NULL,refresh_token_metadata blob DEFAULT NULL,PRIMARY KEY (id)
);

3.配置文件

server:port: 9500
spring:security:oauth2:resourceserver:jwt: issuer-uri: http://127.0.0.1:9500  #认证中心端点,作为资源端的配置application:name: oauth2-authdatasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.3.150:31736/ry-cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username: rootpassword: rootdruid:stat-view-servlet:enabled: trueloginUsername: adminloginPassword: 123456initial-size: 5min-idle: 5maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: truemaxPoolPreparedStatementPerConnectionSize: 20filters: stat,slf4jconnectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000

4.放入官方认证html页面


在这个目录下,代码如下

<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><link rel="stylesheet" href=".5.2/css/bootstrap.min.css"integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous"><title>Custom consent page - Consent required</title><style>body {background-color: aliceblue;}</style><script>function cancelConsent() {document.consent_form.reset();document.consent_form.submit();}</script>
</head>
<body>
<div class="container"><div class="py-5"><h1 class="text-center text-primary">App permissions</h1></div><div class="row"><div class="col text-center"><p>The application<span class="font-weight-bold text-primary" th:text="${clientId}"></span>wants to access your account<span class="font-weight-bold" th:text="${principalName}"></span></p></div></div><div class="row pb-3"><div class="col text-center"><p>The following permissions are requested by the above app.<br/>Please reviewthese and consent if you approve.</p></div></div><div class="row"><div class="col text-center"><form name="consent_form" method="post" action="/oauth2/authorize"><input type="hidden" name="client_id" th:value="${clientId}"><input type="hidden" name="state" th:value="${state}"><div th:each="scope: ${scopes}" class="form-group form-check py-1"><input class="form-check-input"type="checkbox"name="scope"th:value="${scope.scope}"th:id="${scope.scope}"><label class="form-check-label font-weight-bold" th:for="${scope.scope}" th:text="${scope.scope}"></label><p class="text-primary" th:text="${scope.description}"></p></div><p th:if="${not #lists.isEmpty(previouslyApprovedScopes)}">You have already granted the following permissions to the above app:</p><div th:each="scope: ${previouslyApprovedScopes}" class="form-group form-check py-1"><input class="form-check-input"type="checkbox"th:id="${scope.scope}"disabledchecked><label class="form-check-label font-weight-bold" th:for="${scope.scope}" th:text="${scope.scope}"></label><p class="text-primary" th:text="${scope.description}"></p></div><div class="form-group pt-3"><button class="btn btn-primary btn-lg" type="submit" id="submit-consent">Submit Consent</button></div><div class="form-group"><button class="btn btn-link regular" type="button" id="cancel-consent" onclick="cancelConsent();">Cancel</button></div></form></div></div><div class="row pt-4"><div class="col text-center"><p><small>Your consent to provide access is required.<br/>If you do not approve, click Cancel, in which case no information will be shared with the app.</small></p></div></div>
</div>
</body>
</html>

5.生成jks文件

windows下CMD命令窗口输入

keytool -genkeypair -alias shy_debug.jks -keyalg RSA -validity 7 -keystore shy_debug.jks

alias别名
然后根据提示输入相关信息,记好密码和别名,后面要用到
把生成的jks文件放到这里

6.配置KeyPair

@Configuration
public class KeyPairConfig {@Beanpublic KeyPair keyPair() throws Exception {ClassPathResource ksFile = new ClassPathResource("shy_debug.jks");//文件名KeyStoreKeyFactory ksFactory = new KeyStoreKeyFactory(ksFile, "haiwei".toCharArray());  //第二个参数就是生成时候的密码return ksFactory.getKeyPair("shy_debug.jks");}
}

7.配置AuthorizationServerConfig授权服务器配置

@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfig {private static final String CUSTOM_CONSENT_PAGE_URI = "/oauth2/consent";@Autowiredprivate KeyPair keyPair;@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =new OAuth2AuthorizationServerConfigurer<>();authorizationServerConfigurer.authorizationEndpoint(authorizationEndpoint ->authorizationEndpoint.consentPage(CUSTOM_CONSENT_PAGE_URI));RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();http.requestMatcher(endpointsMatcher).authorizeRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated()).csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher)).apply(authorizationServerConfigurer);return http.formLogin(Customizer.withDefaults()).build();}// @formatter:off           jdbc相关@Beanpublic RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {// Save registered client in db as if in-jdbc// 注意这里启动第一次写入客户端信息后一定要注释掉,不然每次启动都会新生成一个重复的客户端,后续会报错。如果已经报错,检擦数据库是否有重复数据,删掉重复的RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString()).clientId("messaging-client").clientSecret("{noop}secret").clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC).authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN).authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).redirectUri("http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc").redirectUri("").scope(OidcScopes.OPENID).scope("message.read").scope("message.write").clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();// Save registered client in db as if in-memoryJdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);registeredClientRepository.save(registeredClient);return registeredClientRepository;}// @formatter:on@Beanpublic OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);}@Beanpublic OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);}@Beanpublic JWKSource<SecurityContext> jwkSource() {RSAKey rsaKey = generateRsa();JWKSet jwkSet = new JWKSet(rsaKey);return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);}public RSAKey generateRsa() {RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();// @formatter:offreturn new RSAKey.Builder(publicKey).privateKey(privateKey).keyID(UUID.randomUUID().toString()).build();// @formatter:on}@Beanpublic ProviderSettings providerSettings() {return ProviderSettings.builder().issuer("http://127.0.0.1:9500").build();}}

8.配置WebSecurityConfig基础security配置

@Configuration
@EnableWebSecurity(debug = true)
public class WebSecurityConfig {@Beanpublic SecurityFilterChain httpSecurityFilterChain(HttpSecurity httpSecurity) throws Exception {httpSecurity.authorizeRequests(authorizeRequests ->authorizeRequests.anyRequest().authenticated()).formLogin(withDefaults());return httpSecurity.build();}/*private Converter<Jwt, AbstractAuthenticationToken> customJwtAuthenticationTokenConverter() {return jwt -> {List<String> userAuthorities = jwt.getClaimAsStringList("authorities");List<String> scopes = jwt.getClaimAsStringList("scope");List<GrantedAuthority> combinedAuthorities = Stream.concat(userAuthorities.stream(),scopes.stream().map(scope -> "SCOPE_" + scope)).map(SimpleGrantedAuthority::new).collect(Collectors.toList());String username = jwt.getClaimAsString("user_name");return new UsernamePasswordAuthenticationToken(username, null, combinedAuthorities);};}*/// @formatter:off@BeanUserDetailsService users() {UserDetails user = User.withDefaultPasswordEncoder().username("admin").password("111111").roles("USER").authorities("test").build();return new InMemoryUserDetailsManager(user);}// @formatter:on
}

9.配置AuthorizationConsentController

@Controller
public class AuthorizationConsentController {private final RegisteredClientRepository registeredClientRepository;private final OAuth2AuthorizationConsentService authorizationConsentService;public AuthorizationConsentController(RegisteredClientRepository registeredClientRepository,OAuth2AuthorizationConsentService authorizationConsentService) {this.registeredClientRepository = registeredClientRepository;this.authorizationConsentService = authorizationConsentService;}@GetMapping(value = "/oauth2/consent")public String consent(Principal principal, Model model,@RequestParam(OAuth2ParameterNames.CLIENT_ID) String clientId,@RequestParam(OAuth2ParameterNames.SCOPE) String scope,@RequestParam(OAuth2ParameterNames.STATE) String state) {// Remove scopes that were already approvedSet<String> scopesToApprove = new HashSet<>();Set<String> previouslyApprovedScopes = new HashSet<>();RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId);OAuth2AuthorizationConsent currentAuthorizationConsent =this.authorizationConsentService.findById(registeredClient.getId(), principal.getName());Set<String> authorizedScopes;if (currentAuthorizationConsent != null) {authorizedScopes = currentAuthorizationConsent.getScopes();} else {authorizedScopes = Collections.emptySet();}for (String requestedScope : StringUtils.delimitedListToStringArray(scope, " ")) {if (authorizedScopes.contains(requestedScope)) {previouslyApprovedScopes.add(requestedScope);} else {scopesToApprove.add(requestedScope);}}model.addAttribute("clientId", clientId);model.addAttribute("state", state);model.addAttribute("scopes", withDescription(scopesToApprove));model.addAttribute("previouslyApprovedScopes", withDescription(previouslyApprovedScopes));model.addAttribute("principalName", principal.getName());return "consent";}private static Set<ScopeWithDescription> withDescription(Set<String> scopes) {Set<ScopeWithDescription> scopeWithDescriptions = new HashSet<>();for (String scope : scopes) {scopeWithDescriptions.add(new ScopeWithDescription(scope));}return scopeWithDescriptions;}public static class ScopeWithDescription {private static final String DEFAULT_DESCRIPTION = "UNKNOWN SCOPE - We cannot provide information about this permission, use caution when granting this.";private static final Map<String, String> scopeDescriptions = new HashMap<>();static {scopeDescriptions.put("message.read","This application will be able to read your message.");scopeDescriptions.put("message.write","This application will be able to add new messages. It will also be able to edit and delete existing messages.");scopeDescriptions.put("other.scope","This is another scope example of a scope description.");}public final String scope;public final String description;ScopeWithDescription(String scope) {this.scope = scope;this.description = scopeDescriptions.getOrDefault(scope, DEFAULT_DESCRIPTION);}}
}

至此配置完毕

三 启动项目,这里我们测试授权码模式

浏览器输入 http://127.0.0.1:9500/oauth2/authorize?client_id=messaging-client&response_type=code&scope=message.read&redirect_uri=
会转到登陆页面

输入账号密码会跳转到认证授权页面

勾选上scope,点认证

这样就获取到code了
然后用postman请求


这样就获取到token了

总结

spring authorization server搭建完成,后面一篇文章我会说明资源服务器端如何接入认证服务器,资源服务器文章链接
spring authorization server授权服务器教程,资源服务器搭建接入认证服务器

更多推荐

spring authorization server授权服务器教程,集成jdbc,使用版本0.2.2

本文发布于:2024-02-25 09:16:31,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1698487.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:版本   服务器   教程   authorization   spring

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!