当前位置: 首页 > news >正文

新潮远网站建设西安建设银行工作招聘网站

新潮远网站建设,西安建设银行工作招聘网站,wordpress 禁止头像,私人做网站a怎么使用Spring Data Redis实现Spring Authorization Server的核心services#xff1f; 本文对应的是文档中的How-to: Implement core services with JPA#xff0c;文档中使用Jpa实现了核心的三个服务类#xff1a;授权信息、客户端信息和授权确认的服务#xff1b;本文会…怎么使用Spring Data Redis实现Spring Authorization Server的核心services 本文对应的是文档中的How-to: Implement core services with JPA文档中使用Jpa实现了核心的三个服务类授权信息、客户端信息和授权确认的服务本文会使用Spring Data Redis参考文档来添加新的实现。在这里也放一下文档中的一句话 本指南的目的是为您自己实现这些服务提供一个起点以便您可以根据自己的需要进行修改。 实现步骤 因为本文使用的是Spring Data所以需要先定义对应的实体然后根据实体定义对应的Repository(Spring Data Repository)最后实现核心的service使用这些Repository操作Redis。 定义实体定义Redis Repositories实现核心服务类 具体实现 定义实体 标题中的类是框架中对应的默认实体下方代码中的类都是从标题后边的类中映射数据从而保存至Redis。 客户端实体(RegisteredClient) package com.example.entity.security;import lombok.Data; import org.springframework.data.annotation.Id; import org.springframework.data.redis.core.RedisHash; import org.springframework.data.redis.core.index.Indexed;import java.io.Serializable; import java.time.Instant;/*** 基于redis存储的客户端实体** author vains*/ Data RedisHash(value client) public class RedisRegisteredClient implements Serializable {/*** 主键*/Idprivate String id;/*** 客户端id*/Indexedprivate String clientId;/*** 客户端id签发时间*/private Instant clientIdIssuedAt;/*** 客户端秘钥*/private String clientSecret;/*** 客户端秘钥过期时间*/private Instant clientSecretExpiresAt;/*** 客户端名称*/private String clientName;/*** 客户端支持的认证方式*/private String clientAuthenticationMethods;/*** 客户端支持的授权申请方式*/private String authorizationGrantTypes;/*** 回调地址*/private String redirectUris;/*** 登出回调地址*/private String postLogoutRedirectUris;/*** 客户端拥有的scope*/private String scopes;/*** 客户端配置*/private String clientSettings;/*** 通过该客户端签发的access token设置*/private String tokenSettings;}授权信息实体(OAuth2Authorization) 该类中包括了授权码、access_token、refresh_token、设备码和id_token等数据。 package com.example.entity.security;import lombok.Data; import org.springframework.data.annotation.Id; import org.springframework.data.redis.core.RedisHash; import org.springframework.data.redis.core.TimeToLive; import org.springframework.data.redis.core.index.Indexed;import java.io.Serializable; import java.time.Instant; import java.util.concurrent.TimeUnit;/*** 使用Repository将授权申请的认证信息缓存至redis的实体** author vains*/ Data RedisHash(value authorization) public class RedisOAuth2Authorization implements Serializable {/*** 主键*/Idprivate String id;/*** 授权申请时使用的客户端id*/private String registeredClientId;/*** 授权用户姓名*/private String principalName;/*** 授权申请时使用 grant_type*/private String authorizationGrantType;/*** 授权申请的scope*/private String authorizedScopes;/*** 授权的认证信息(当前用户)、请求信息(授权申请请求)*/private String attributes;/*** 授权申请时的state*/Indexedprivate String state;/*** 授权码的值*/Indexedprivate String authorizationCodeValue;/*** 授权码签发时间*/private Instant authorizationCodeIssuedAt;/*** 授权码过期时间*/private Instant authorizationCodeExpiresAt;/*** 授权码元数据*/private String authorizationCodeMetadata;/*** access token的值*/Indexedprivate String accessTokenValue;/*** access token签发时间*/private Instant accessTokenIssuedAt;/*** access token过期时间*/private Instant accessTokenExpiresAt;/*** access token元数据*/private String accessTokenMetadata;/*** access token的类型*/private String accessTokenType;/*** access token中包含的scope*/private String accessTokenScopes;/*** refresh token的值*/Indexedprivate String refreshTokenValue;/*** refresh token签发使劲*/private Instant refreshTokenIssuedAt;/*** refresh token过期时间*/private Instant refreshTokenExpiresAt;/*** refresh token元数据*/private String refreshTokenMetadata;/*** id token的值*/Indexedprivate String oidcIdTokenValue;/*** id token签发时间*/private Instant oidcIdTokenIssuedAt;/*** id token过期时间*/private Instant oidcIdTokenExpiresAt;/*** id token元数据*/private String oidcIdTokenMetadata;/*** id token中包含的属性*/private String oidcIdTokenClaims;/*** 用户码的值*/Indexedprivate String userCodeValue;/*** 用户码签发时间*/private Instant userCodeIssuedAt;/*** 用户码过期时间*/private Instant userCodeExpiresAt;/*** 用户码元数据*/private String userCodeMetadata;/*** 设备码的值*/Indexedprivate String deviceCodeValue;/*** 设备码签发时间*/private Instant deviceCodeIssuedAt;/*** 设备码过期时间*/private Instant deviceCodeExpiresAt;/*** 设备码元数据*/private String deviceCodeMetadata;/*** 当前对象在Redis中的过期时间*/TimeToLive(unit TimeUnit.MINUTES)private Long timeout;}授权确认信息实体(OAuth2AuthorizationConsent) package com.example.entity.security;import lombok.Data; import org.springframework.data.annotation.Id; import org.springframework.data.redis.core.RedisHash; import org.springframework.data.redis.core.index.Indexed;import java.io.Serializable;/*** 基于redis的授权确认存储实体** author vains*/ Data RedisHash(value authorizationConsent) public class RedisAuthorizationConsent implements Serializable {/*** 额外提供的主键*/Idprivate String id;/*** 当前授权确认的客户端id*/Indexedprivate String registeredClientId;/*** 当前授权确认用户的 username*/Indexedprivate String principalName;/*** 授权确认的scope*/private String authorities;}注解解释 RedisHash标注这是一个Spring Data Redis的实体类同时也指定了该类保存在Redis时的key前缀。Id指定id属性id属性也会被当做key的一部分本注解和上个这两个注解项负责创建用于持久化哈希的实际键。Indexed注解标注的字段会创建一个基于该字段的索引让Repository支持findBy被注解标注的字段名等方法。TimeToLive注解标注的字段会被用来当做该对象在Redis的过期时间虽然RedisHash也支持设置过期时间但是不够灵活所以额外添加一个字段针对某条数据设置过期时间。 定义Spring Data Repositories(Redis Repositories) Spring Data Repository是Spring Data抽象出来的一个增删改查的interface接口适用于Spring Data的不同实现框架提供了支持增删改查的公共Repository CrudRepository实体类主键类型。 客户端Repository 像下边的接口中有一个findByClientId方法但是ClientId属性并不是主键如果不加Indexed注解则该方法就不会生效。 package com.example.repository;import com.example.entity.security.RedisRegisteredClient; import org.springframework.data.repository.CrudRepository;import java.util.Optional;/*** 基于Spring Data Redis的客户端repository** author vains*/ public interface RedisClientRepository extends CrudRepositoryRedisRegisteredClient, String {/*** 根据客户端Id查询客户端信息** param clientId 客户端id* return 客户端信息*/OptionalRedisRegisteredClient findByClientId(String clientId);}授权信息Repository 提供根据state, authorizationCodeValue, accessTokenValue, refreshTokenValue, userCodeValue 和 deviceCodeValue属性查询的方法在service中组合使用。 package com.example.repository;import com.example.entity.security.RedisOAuth2Authorization; import org.springframework.data.repository.CrudRepository;import java.util.Optional;/*** oauth2授权管理** author vains*/ public interface RedisOAuth2AuthorizationRepository extends CrudRepositoryRedisOAuth2Authorization, String {/*** 根据授权码获取认证信息** param token 授权码* return 认证信息*/OptionalRedisOAuth2Authorization findByAuthorizationCodeValue(String token);/*** 根据access token获取认证信息** param token access token* return 认证信息*/OptionalRedisOAuth2Authorization findByAccessTokenValue(String token);/*** 根据刷新token获取认证信息** param token 刷新token* return 认证信息*/OptionalRedisOAuth2Authorization findByRefreshTokenValue(String token);/*** 根据id token获取认证信息** param token id token* return 认证信息*/OptionalRedisOAuth2Authorization findByOidcIdTokenValue(String token);/*** 根据用户码获取认证信息** param token 用户码* return 认证信息*/OptionalRedisOAuth2Authorization findByUserCodeValue(String token);/*** 根据设备码获取认证信息** param token 设备码* return 认证信息*/OptionalRedisOAuth2Authorization findByDeviceCodeValue(String token);/*** 根据state获取认证信息** param token 授权申请时的state* return 认证信息*/OptionalRedisOAuth2Authorization findByState(String token); }授权确认信息Repository 提供一个根据客户端Id和授权确认用户的username查询的方法。 package com.example.repository;import com.example.entity.security.RedisAuthorizationConsent; import org.springframework.data.repository.CrudRepository;import java.util.Optional;/*** 基于redis的授权确认repository** author vains*/ public interface RedisAuthorizationConsentRepository extends CrudRepositoryRedisAuthorizationConsent, String {/*** 根据客户端id和授权确认用户的 username 查询授权确认信息** param registeredClientId 客户端id* param principalName 授权确认用户的 username* return 授权确认记录*/OptionalRedisAuthorizationConsent findByRegisteredClientIdAndPrincipalName(String registeredClientId, String principalName);} 以下内容摘抄自文档内容 查询方法允许从方法名自动派生简单的查找器查询请确保在查找器方法中使用的属性已设置为索引。 下表提供了Redis支持的关键字概述以及包含该关键字的方法本质上是什么: KeywordSampleRedis snippetAndfindByLastnameAndFirstnameSINTER …:firstname:rand …:lastname:al’thorOrfindByLastnameOrFirstnameSUNION …:firstname:rand …:lastname:al’thorIs, EqualsfindByFirstname, findByFirstnameIs, findByFirstnameEqualsSINTER …:firstname:randIsTrueFindByAliveIsTrueSINTER …:alive:1IsFalsefindByAliveIsFalseSINTER …:alive:0Top,FirstfindFirst10ByFirstname,findTop5ByFirstname 实现核心service 客户端Repository(RegisteredClientRepository) 小tip我也不知道为什么这个这么特殊是Repository… package com.example.repository;import com.example.entity.security.RedisRegisteredClient; import com.example.service.impl.RedisOAuth2AuthorizationService; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.jackson2.SecurityJackson2Modules; 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.jose.jws.SignatureAlgorithm; 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.jackson2.OAuth2AuthorizationServerJackson2Module; import org.springframework.security.oauth2.server.authorization.settings.ClientSettings; import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat; import org.springframework.security.oauth2.server.authorization.settings.TokenSettings; import org.springframework.stereotype.Repository; import org.springframework.util.Assert; import org.springframework.util.StringUtils;import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID;/*** 基于redis的客户端repository实现** author vains*/ Slf4j Repository RequiredArgsConstructor public class RedisRegisteredClientRepository implements RegisteredClientRepository {/*** 提供给客户端初始化使用(不需要可删除)*/private final PasswordEncoder passwordEncoder;private final RedisClientRepository repository;private final static ObjectMapper MAPPER new ObjectMapper();static {// 初始化序列化配置ClassLoader classLoader RedisOAuth2AuthorizationService.class.getClassLoader();// 加载security提供的ModulesListModule modules SecurityJackson2Modules.getModules(classLoader);MAPPER.registerModules(modules);// 加载Authorization Server提供的ModuleMAPPER.registerModule(new OAuth2AuthorizationServerJackson2Module());}Overridepublic void save(RegisteredClient registeredClient) {Assert.notNull(registeredClient, registeredClient cannot be null);this.repository.findByClientId(registeredClient.getClientId()).ifPresent(existingRegisteredClient - this.repository.deleteById(existingRegisteredClient.getId()));this.repository.save(toEntity(registeredClient));}Overridepublic RegisteredClient findById(String id) {Assert.hasText(id, id cannot be empty);return this.repository.findById(id).map(this::toObject).orElse(null);}Overridepublic RegisteredClient findByClientId(String clientId) {Assert.hasText(clientId, clientId cannot be empty);return this.repository.findByClientId(clientId).map(this::toObject).orElse(null);}private RegisteredClient toObject(RedisRegisteredClient client) {SetString clientAuthenticationMethods StringUtils.commaDelimitedListToSet(client.getClientAuthenticationMethods());SetString authorizationGrantTypes StringUtils.commaDelimitedListToSet(client.getAuthorizationGrantTypes());SetString redirectUris StringUtils.commaDelimitedListToSet(client.getRedirectUris());SetString postLogoutRedirectUris StringUtils.commaDelimitedListToSet(client.getPostLogoutRedirectUris());SetString clientScopes StringUtils.commaDelimitedListToSet(client.getScopes());RegisteredClient.Builder builder RegisteredClient.withId(client.getId()).clientId(client.getClientId()).clientIdIssuedAt(client.getClientIdIssuedAt()).clientSecret(client.getClientSecret()).clientSecretExpiresAt(client.getClientSecretExpiresAt()).clientName(client.getClientName()).clientAuthenticationMethods(authenticationMethods -clientAuthenticationMethods.forEach(authenticationMethod -authenticationMethods.add(resolveClientAuthenticationMethod(authenticationMethod)))).authorizationGrantTypes((grantTypes) -authorizationGrantTypes.forEach(grantType -grantTypes.add(resolveAuthorizationGrantType(grantType)))).redirectUris((uris) - uris.addAll(redirectUris)).postLogoutRedirectUris((uris) - uris.addAll(postLogoutRedirectUris)).scopes((scopes) - scopes.addAll(clientScopes));MapString, Object clientSettingsMap parseMap(client.getClientSettings());builder.clientSettings(ClientSettings.withSettings(clientSettingsMap).build());MapString, Object tokenSettingsMap parseMap(client.getTokenSettings());builder.tokenSettings(TokenSettings.withSettings(tokenSettingsMap).build());return builder.build();}private RedisRegisteredClient toEntity(RegisteredClient registeredClient) {ListString clientAuthenticationMethods new ArrayList(registeredClient.getClientAuthenticationMethods().size());registeredClient.getClientAuthenticationMethods().forEach(clientAuthenticationMethod -clientAuthenticationMethods.add(clientAuthenticationMethod.getValue()));ListString authorizationGrantTypes new ArrayList(registeredClient.getAuthorizationGrantTypes().size());registeredClient.getAuthorizationGrantTypes().forEach(authorizationGrantType -authorizationGrantTypes.add(authorizationGrantType.getValue()));RedisRegisteredClient entity new RedisRegisteredClient();entity.setId(registeredClient.getId());entity.setClientId(registeredClient.getClientId());entity.setClientIdIssuedAt(registeredClient.getClientIdIssuedAt());entity.setClientSecret(registeredClient.getClientSecret());entity.setClientSecretExpiresAt(registeredClient.getClientSecretExpiresAt());entity.setClientName(registeredClient.getClientName());entity.setClientAuthenticationMethods(StringUtils.collectionToCommaDelimitedString(clientAuthenticationMethods));entity.setAuthorizationGrantTypes(StringUtils.collectionToCommaDelimitedString(authorizationGrantTypes));entity.setRedirectUris(StringUtils.collectionToCommaDelimitedString(registeredClient.getRedirectUris()));entity.setPostLogoutRedirectUris(StringUtils.collectionToCommaDelimitedString(registeredClient.getPostLogoutRedirectUris()));entity.setScopes(StringUtils.collectionToCommaDelimitedString(registeredClient.getScopes()));entity.setClientSettings(writeMap(registeredClient.getClientSettings().getSettings()));entity.setTokenSettings(writeMap(registeredClient.getTokenSettings().getSettings()));return entity;}private MapString, Object parseMap(String data) {try {return MAPPER.readValue(data, new TypeReference() {});} catch (Exception ex) {throw new IllegalArgumentException(ex.getMessage(), ex);}}private String writeMap(MapString, Object data) {try {return MAPPER.writeValueAsString(data);} catch (Exception ex) {throw new IllegalArgumentException(ex.getMessage(), ex);}}private static AuthorizationGrantType resolveAuthorizationGrantType(String authorizationGrantType) {if (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(authorizationGrantType)) {return AuthorizationGrantType.AUTHORIZATION_CODE;} else if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equals(authorizationGrantType)) {return AuthorizationGrantType.CLIENT_CREDENTIALS;} else if (AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(authorizationGrantType)) {return AuthorizationGrantType.REFRESH_TOKEN;}// Custom authorization grant typereturn new AuthorizationGrantType(authorizationGrantType);}private static ClientAuthenticationMethod resolveClientAuthenticationMethod(String clientAuthenticationMethod) {if (ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue().equals(clientAuthenticationMethod)) {return ClientAuthenticationMethod.CLIENT_SECRET_BASIC;} else if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientAuthenticationMethod)) {return ClientAuthenticationMethod.CLIENT_SECRET_POST;} else if (ClientAuthenticationMethod.NONE.getValue().equals(clientAuthenticationMethod)) {return ClientAuthenticationMethod.NONE;}// Custom client authentication methodreturn new ClientAuthenticationMethod(clientAuthenticationMethod);}/*** 容器启动后初始化客户端* (不需要可删除)*/PostConstructpublic void initClients() {log.info(Initialize client information to Redis.);// 默认需要授权确认ClientSettings.Builder builder ClientSettings.builder().requireAuthorizationConsent(Boolean.TRUE);TokenSettings tokenSettings TokenSettings.builder()// 自包含token(jwt).accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)// Access Token 存活时间2小时.accessTokenTimeToLive(Duration.ofHours(2L))// 授权码存活时间5分钟.authorizationCodeTimeToLive(Duration.ofMinutes(5L))// 设备码存活时间5分钟.deviceCodeTimeToLive(Duration.ofMinutes(5L))// Refresh Token 存活时间7天.refreshTokenTimeToLive(Duration.ofDays(7L))// 刷新 Access Token 后是否重用 Refresh Token.reuseRefreshTokens(Boolean.TRUE)// 设置 Id Token 加密方式.idTokenSignatureAlgorithm(SignatureAlgorithm.RS256).build();// 正常授权码客户端RegisteredClient registeredClient RegisteredClient.withId(UUID.randomUUID().toString())// 客户端id.clientId(messaging-client)// 客户端名称.clientName(授权码)// 客户端秘钥使用密码解析器加密.clientSecret(passwordEncoder.encode(123456))// 客户端认证方式基于请求头的认证.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)// 配置资源服务器使用该客户端获取授权时支持的方式.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN).authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)// 授权码模式回调地址oauth2.1已改为精准匹配不能只设置域名并且屏蔽了localhost本机使用127.0.0.1访问.redirectUri(http://127.0.0.1:8000/login/oauth2/code/messaging-client-oidc).redirectUri(https://www.baidu.com)// 该客户端的授权范围OPENID与PROFILE是IdToken的scope获取授权时请求OPENID的scope时认证服务会返回IdToken.scope(OidcScopes.OPENID).scope(OidcScopes.PROFILE)// 指定scope.scope(message.read).scope(message.write)// 客户端设置设置用户需要确认授权.clientSettings(builder.build())// token相关配置.tokenSettings(tokenSettings).build();// 设备码授权客户端RegisteredClient deviceClient RegisteredClient.withId(UUID.randomUUID().toString()).clientId(device-message-client).clientName(普通公共客户端)// 公共客户端.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)// 设备码授权.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE).authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)// 指定scope.scope(message.read).scope(message.write)// token相关配置.tokenSettings(tokenSettings).build();// PKCE客户端RegisteredClient pkceClient RegisteredClient.withId(UUID.randomUUID().toString()).clientId(pkce-message-client).clientName(PKCE流程)// 公共客户端.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)// 设备码授权.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)// 授权码模式回调地址oauth2.1已改为精准匹配不能只设置域名并且屏蔽了localhost本机使用127.0.0.1访问.redirectUri(http://127.0.0.1:8000/login/oauth2/code/messaging-client-oidc)// 开启 PKCE 流程.clientSettings(builder.requireProofKey(Boolean.TRUE).build())// 指定scope.scope(message.read).scope(message.write)// token相关配置.tokenSettings(tokenSettings).build();// 初始化客户端this.save(registeredClient);this.save(deviceClient);this.save(pkceClient);}}类中初始化客户端信息的操作针对第一次使用启动的项目同时每次启动也是更新客户端的操作如果不需要读者可自行去除。 授权信息的service package com.example.service.impl;import com.example.entity.security.RedisOAuth2Authorization; import com.example.repository.RedisOAuth2AuthorizationRepository; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import org.springframework.dao.DataRetrievalFailureException; import org.springframework.security.jackson2.SecurityJackson2Modules; import org.springframework.security.oauth2.core.*; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.core.oidc.OidcIdToken; import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames; import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; 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.jackson2.OAuth2AuthorizationServerJackson2Module; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import org.springframework.util.StringUtils;import java.time.Duration; import java.time.Instant; import java.util.*; import java.util.function.Consumer;/*** 基于redis的授权管理服务** author vains*/ Service RequiredArgsConstructor public class RedisOAuth2AuthorizationService implements OAuth2AuthorizationService {private final RegisteredClientRepository registeredClientRepository;private final RedisOAuth2AuthorizationRepository oAuth2AuthorizationRepository;private final static ObjectMapper MAPPER new ObjectMapper();static {// 初始化序列化配置ClassLoader classLoader RedisOAuth2AuthorizationService.class.getClassLoader();// 加载security提供的ModulesListModule modules SecurityJackson2Modules.getModules(classLoader);MAPPER.registerModules(modules);// 加载Authorization Server提供的ModuleMAPPER.registerModule(new OAuth2AuthorizationServerJackson2Module());}Overridepublic void save(OAuth2Authorization authorization) {OptionalRedisOAuth2Authorization existingAuthorization oAuth2AuthorizationRepository.findById(authorization.getId());// 如果已存在则删除后再保存existingAuthorization.map(RedisOAuth2Authorization::getId).ifPresent(oAuth2AuthorizationRepository::deleteById);// 过期时间默认永不过期long maxTimeout -1L;// 所有code的过期时间方便计算最大值ListInstant expiresAtList new ArrayList();RedisOAuth2Authorization entity toEntity(authorization);// 如果有过期时间就存入Optional.ofNullable(entity.getAuthorizationCodeExpiresAt()).ifPresent(expiresAtList::add);// 如果有过期时间就存入Optional.ofNullable(entity.getAccessTokenExpiresAt()).ifPresent(expiresAtList::add);// 如果有过期时间就存入Optional.ofNullable(entity.getRefreshTokenExpiresAt()).ifPresent(expiresAtList::add);// 如果有过期时间就存入Optional.ofNullable(entity.getOidcIdTokenExpiresAt()).ifPresent(expiresAtList::add);// 如果有过期时间就存入Optional.ofNullable(entity.getUserCodeExpiresAt()).ifPresent(expiresAtList::add);// 如果有过期时间就存入Optional.ofNullable(entity.getDeviceCodeExpiresAt()).ifPresent(expiresAtList::add);// 获取最大的日期OptionalInstant maxInstant expiresAtList.stream().max(Comparator.comparing(Instant::getEpochSecond));if (maxInstant.isPresent()) {// 计算时间差Duration between Duration.between(Instant.now(), maxInstant.get());// 转为分钟maxTimeout between.toMinutes();}// 设置过期时间entity.setTimeout(maxTimeout);// 保存至redisoAuth2AuthorizationRepository.save(entity);}Overridepublic void remove(OAuth2Authorization authorization) {Assert.notNull(authorization, authorization cannot be null);oAuth2AuthorizationRepository.deleteById(authorization.getId());}Overridepublic OAuth2Authorization findById(String id) {Assert.hasText(id, id cannot be empty);return oAuth2AuthorizationRepository.findById(id).map(this::toObject).orElse(null);}Overridepublic OAuth2Authorization findByToken(String token, OAuth2TokenType tokenType) {Assert.hasText(token, token cannot be empty);OptionalRedisOAuth2Authorization result;if (tokenType null) {result oAuth2AuthorizationRepository.findByState(token).or(() - oAuth2AuthorizationRepository.findByAuthorizationCodeValue(token)).or(() - oAuth2AuthorizationRepository.findByAccessTokenValue(token)).or(() - oAuth2AuthorizationRepository.findByOidcIdTokenValue(token)).or(() - oAuth2AuthorizationRepository.findByRefreshTokenValue(token)).or(() - oAuth2AuthorizationRepository.findByUserCodeValue(token)).or(() - oAuth2AuthorizationRepository.findByDeviceCodeValue(token));} else if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) {result oAuth2AuthorizationRepository.findByState(token);} else if (OAuth2ParameterNames.CODE.equals(tokenType.getValue())) {result oAuth2AuthorizationRepository.findByAuthorizationCodeValue(token);} else if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenType)) {result oAuth2AuthorizationRepository.findByAccessTokenValue(token);} else if (OidcParameterNames.ID_TOKEN.equals(tokenType.getValue())) {result oAuth2AuthorizationRepository.findByOidcIdTokenValue(token);} else if (OAuth2TokenType.REFRESH_TOKEN.equals(tokenType)) {result oAuth2AuthorizationRepository.findByRefreshTokenValue(token);} else if (OAuth2ParameterNames.USER_CODE.equals(tokenType.getValue())) {result oAuth2AuthorizationRepository.findByUserCodeValue(token);} else if (OAuth2ParameterNames.DEVICE_CODE.equals(tokenType.getValue())) {result oAuth2AuthorizationRepository.findByDeviceCodeValue(token);} else {result Optional.empty();}return result.map(this::toObject).orElse(null);}/*** 将redis中存储的类型转为框架所需的类型** param entity redis中存储的类型* return 框架所需的类型*/private OAuth2Authorization toObject(RedisOAuth2Authorization entity) {RegisteredClient registeredClient this.registeredClientRepository.findById(entity.getRegisteredClientId());if (registeredClient null) {throw new DataRetrievalFailureException(The RegisteredClient with id entity.getRegisteredClientId() was not found in the RegisteredClientRepository.);}OAuth2Authorization.Builder builder OAuth2Authorization.withRegisteredClient(registeredClient).id(entity.getId()).principalName(entity.getPrincipalName()).authorizationGrantType(resolveAuthorizationGrantType(entity.getAuthorizationGrantType())).authorizedScopes(StringUtils.commaDelimitedListToSet(entity.getAuthorizedScopes())).attributes(attributes - attributes.putAll(parseMap(entity.getAttributes())));if (entity.getState() ! null) {builder.attribute(OAuth2ParameterNames.STATE, entity.getState());}if (entity.getAuthorizationCodeValue() ! null) {OAuth2AuthorizationCode authorizationCode new OAuth2AuthorizationCode(entity.getAuthorizationCodeValue(),entity.getAuthorizationCodeIssuedAt(),entity.getAuthorizationCodeExpiresAt());builder.token(authorizationCode, metadata - metadata.putAll(parseMap(entity.getAuthorizationCodeMetadata())));}if (entity.getAccessTokenValue() ! null) {OAuth2AccessToken accessToken new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,entity.getAccessTokenValue(),entity.getAccessTokenIssuedAt(),entity.getAccessTokenExpiresAt(),StringUtils.commaDelimitedListToSet(entity.getAccessTokenScopes()));builder.token(accessToken, metadata - metadata.putAll(parseMap(entity.getAccessTokenMetadata())));}if (entity.getRefreshTokenValue() ! null) {OAuth2RefreshToken refreshToken new OAuth2RefreshToken(entity.getRefreshTokenValue(),entity.getRefreshTokenIssuedAt(),entity.getRefreshTokenExpiresAt());builder.token(refreshToken, metadata - metadata.putAll(parseMap(entity.getRefreshTokenMetadata())));}if (entity.getOidcIdTokenValue() ! null) {OidcIdToken idToken new OidcIdToken(entity.getOidcIdTokenValue(),entity.getOidcIdTokenIssuedAt(),entity.getOidcIdTokenExpiresAt(),parseMap(entity.getOidcIdTokenClaims()));builder.token(idToken, metadata - metadata.putAll(parseMap(entity.getOidcIdTokenMetadata())));}if (entity.getUserCodeValue() ! null) {OAuth2UserCode userCode new OAuth2UserCode(entity.getUserCodeValue(),entity.getUserCodeIssuedAt(),entity.getUserCodeExpiresAt());builder.token(userCode, metadata - metadata.putAll(parseMap(entity.getUserCodeMetadata())));}if (entity.getDeviceCodeValue() ! null) {OAuth2DeviceCode deviceCode new OAuth2DeviceCode(entity.getDeviceCodeValue(),entity.getDeviceCodeIssuedAt(),entity.getDeviceCodeExpiresAt());builder.token(deviceCode, metadata - metadata.putAll(parseMap(entity.getDeviceCodeMetadata())));}return builder.build();}/*** 将框架所需的类型转为redis中存储的类型** param authorization 框架所需的类型* return redis中存储的类型*/private RedisOAuth2Authorization toEntity(OAuth2Authorization authorization) {RedisOAuth2Authorization entity new RedisOAuth2Authorization();entity.setId(authorization.getId());entity.setRegisteredClientId(authorization.getRegisteredClientId());entity.setPrincipalName(authorization.getPrincipalName());entity.setAuthorizationGrantType(authorization.getAuthorizationGrantType().getValue());entity.setAuthorizedScopes(StringUtils.collectionToDelimitedString(authorization.getAuthorizedScopes(), ,));entity.setAttributes(writeMap(authorization.getAttributes()));entity.setState(authorization.getAttribute(OAuth2ParameterNames.STATE));OAuth2Authorization.TokenOAuth2AuthorizationCode authorizationCode authorization.getToken(OAuth2AuthorizationCode.class);setTokenValues(authorizationCode,entity::setAuthorizationCodeValue,entity::setAuthorizationCodeIssuedAt,entity::setAuthorizationCodeExpiresAt,entity::setAuthorizationCodeMetadata);OAuth2Authorization.TokenOAuth2AccessToken accessToken authorization.getToken(OAuth2AccessToken.class);setTokenValues(accessToken,entity::setAccessTokenValue,entity::setAccessTokenIssuedAt,entity::setAccessTokenExpiresAt,entity::setAccessTokenMetadata);if (accessToken ! null accessToken.getToken().getScopes() ! null) {entity.setAccessTokenScopes(StringUtils.collectionToDelimitedString(accessToken.getToken().getScopes(), ,));}OAuth2Authorization.TokenOAuth2RefreshToken refreshToken authorization.getToken(OAuth2RefreshToken.class);setTokenValues(refreshToken,entity::setRefreshTokenValue,entity::setRefreshTokenIssuedAt,entity::setRefreshTokenExpiresAt,entity::setRefreshTokenMetadata);OAuth2Authorization.TokenOidcIdToken oidcIdToken authorization.getToken(OidcIdToken.class);setTokenValues(oidcIdToken,entity::setOidcIdTokenValue,entity::setOidcIdTokenIssuedAt,entity::setOidcIdTokenExpiresAt,entity::setOidcIdTokenMetadata);if (oidcIdToken ! null) {entity.setOidcIdTokenClaims(writeMap(oidcIdToken.getClaims()));}OAuth2Authorization.TokenOAuth2UserCode userCode authorization.getToken(OAuth2UserCode.class);setTokenValues(userCode,entity::setUserCodeValue,entity::setUserCodeIssuedAt,entity::setUserCodeExpiresAt,entity::setUserCodeMetadata);OAuth2Authorization.TokenOAuth2DeviceCode deviceCode authorization.getToken(OAuth2DeviceCode.class);setTokenValues(deviceCode,entity::setDeviceCodeValue,entity::setDeviceCodeIssuedAt,entity::setDeviceCodeExpiresAt,entity::setDeviceCodeMetadata);return entity;}/*** 设置token的值** param token Token实例* param tokenValueConsumer set方法* param issuedAtConsumer set方法* param expiresAtConsumer set方法* param metadataConsumer set方法*/private void setTokenValues(OAuth2Authorization.Token? token,ConsumerString tokenValueConsumer,ConsumerInstant issuedAtConsumer,ConsumerInstant expiresAtConsumer,ConsumerString metadataConsumer) {if (token ! null) {OAuth2Token oAuth2Token token.getToken();tokenValueConsumer.accept(oAuth2Token.getTokenValue());issuedAtConsumer.accept(oAuth2Token.getIssuedAt());expiresAtConsumer.accept(oAuth2Token.getExpiresAt());metadataConsumer.accept(writeMap(token.getMetadata()));}}/*** 处理授权申请时的 GrantType** param authorizationGrantType 授权申请时的 GrantType* return AuthorizationGrantType的实例*/private static AuthorizationGrantType resolveAuthorizationGrantType(String authorizationGrantType) {if (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(authorizationGrantType)) {return AuthorizationGrantType.AUTHORIZATION_CODE;} else if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equals(authorizationGrantType)) {return AuthorizationGrantType.CLIENT_CREDENTIALS;} else if (AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(authorizationGrantType)) {return AuthorizationGrantType.REFRESH_TOKEN;} else if (AuthorizationGrantType.DEVICE_CODE.getValue().equals(authorizationGrantType)) {return AuthorizationGrantType.DEVICE_CODE;}// Custom authorization grant typereturn new AuthorizationGrantType(authorizationGrantType);}/*** 将json转为map** param data json* return map对象*/private MapString, Object parseMap(String data) {try {return MAPPER.readValue(data, new TypeReference() {});} catch (Exception ex) {throw new IllegalArgumentException(ex.getMessage(), ex);}}/*** 将map对象转为json字符串** param metadata map对象* return json字符串*/private String writeMap(MapString, Object metadata) {try {return MAPPER.writeValueAsString(metadata);} catch (Exception ex) {throw new IllegalArgumentException(ex.getMessage(), ex);}}} 授权确认信息的service package com.example.service.impl;import com.example.entity.security.RedisAuthorizationConsent; import com.example.repository.RedisAuthorizationConsentRepository; import lombok.RequiredArgsConstructor; import org.springframework.dao.DataRetrievalFailureException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService; import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import org.springframework.util.StringUtils;import java.util.HashSet; import java.util.Set; import java.util.UUID;/*** 基于redis的授权确认服务实现** author vains*/ Service RequiredArgsConstructor public class RedisOAuth2AuthorizationConsentService implements OAuth2AuthorizationConsentService {private final RegisteredClientRepository registeredClientRepository;private final RedisAuthorizationConsentRepository authorizationConsentRepository;Overridepublic void save(OAuth2AuthorizationConsent authorizationConsent) {Assert.notNull(authorizationConsent, authorizationConsent cannot be null);// 如果存在就先删除this.authorizationConsentRepository.findByRegisteredClientIdAndPrincipalName(authorizationConsent.getRegisteredClientId(), authorizationConsent.getPrincipalName()).ifPresent(existingConsent - this.authorizationConsentRepository.deleteById(existingConsent.getId()));// 保存RedisAuthorizationConsent entity toEntity(authorizationConsent);entity.setId(UUID.randomUUID().toString());this.authorizationConsentRepository.save(entity);}Overridepublic void remove(OAuth2AuthorizationConsent authorizationConsent) {Assert.notNull(authorizationConsent, authorizationConsent cannot be null);// 如果存在就删除this.authorizationConsentRepository.findByRegisteredClientIdAndPrincipalName(authorizationConsent.getRegisteredClientId(), authorizationConsent.getPrincipalName()).ifPresent(existingConsent - this.authorizationConsentRepository.deleteById(existingConsent.getId()));}Overridepublic OAuth2AuthorizationConsent findById(String registeredClientId, String principalName) {Assert.hasText(registeredClientId, registeredClientId cannot be empty);Assert.hasText(principalName, principalName cannot be empty);return this.authorizationConsentRepository.findByRegisteredClientIdAndPrincipalName(registeredClientId, principalName).map(this::toObject).orElse(null);}private OAuth2AuthorizationConsent toObject(RedisAuthorizationConsent authorizationConsent) {String registeredClientId authorizationConsent.getRegisteredClientId();RegisteredClient registeredClient this.registeredClientRepository.findById(registeredClientId);if (registeredClient null) {throw new DataRetrievalFailureException(The RegisteredClient with id registeredClientId was not found in the RegisteredClientRepository.);}OAuth2AuthorizationConsent.Builder builder OAuth2AuthorizationConsent.withId(registeredClientId, authorizationConsent.getPrincipalName());if (authorizationConsent.getAuthorities() ! null) {for (String authority : StringUtils.commaDelimitedListToSet(authorizationConsent.getAuthorities())) {builder.authority(new SimpleGrantedAuthority(authority));}}return builder.build();}private RedisAuthorizationConsent toEntity(OAuth2AuthorizationConsent authorizationConsent) {RedisAuthorizationConsent entity new RedisAuthorizationConsent();entity.setRegisteredClientId(authorizationConsent.getRegisteredClientId());entity.setPrincipalName(authorizationConsent.getPrincipalName());SetString authorities new HashSet();for (GrantedAuthority authority : authorizationConsent.getAuthorities()) {authorities.add(authority.getAuthority());}entity.setAuthorities(StringUtils.collectionToCommaDelimitedString(authorities));return entity;}}去除认证服务配置文件中这三个核心service的注入 /*** 配置客户端Repository** param jdbcTemplate db 数据源信息* return 基于数据库的repository*/ Bean public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {// 基于db存储客户端还有一个基于内存的实现 InMemoryRegisteredClientRepositoryreturn new JdbcRegisteredClientRepository(jdbcTemplate); }/*** 配置基于db的oauth2的授权管理服务** param jdbcTemplate db数据源信息* param registeredClientRepository 上边注入的客户端repository* return JdbcOAuth2AuthorizationService*/ Bean public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {// 基于db的oauth2认证服务还有一个基于内存的服务实现InMemoryOAuth2AuthorizationServicereturn new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository); }/*** 配置基于db的授权确认管理服务** param jdbcTemplate db数据源信息* param registeredClientRepository 客户端repository* return JdbcOAuth2AuthorizationConsentService*/ Bean public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {// 基于db的授权确认管理服务还有一个基于内存的服务实现InMemoryOAuth2AuthorizationConsentServicereturn new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository); }写在最后 到此为止基本就结束了本文章和前边的所有系列文章没有必要的关联如果是第一次看到文章的读者也是可以很顺畅的将文章中的内容引入项目当然因为引用了Spring Data Redis所以项目必须要先有Redis支持。 文章看起来很长但是实际上就是定义三个实体类定义三个Repository然后实现核心的service逻辑并不复杂操作Redis的内容因为使用了Spring Data Repositories所以这两部分内容很少内容多得地方就在每个service中实体与默认实体的转换中一大堆的转换内容导致文章看起来内容很多但是这些内容在文档中都已经实现所以说这部分内容直接Copy就行哈哈。 附录 How-to: Implement core services with JPASpring Data Redis Redis RepositoriesTimeToLiveIndexed代码仓库Gitee、Github
http://www.pierceye.com/news/466182/

相关文章:

  • 网站建设胶州家园外贸网站怎么注册
  • 我想找阿里巴巴做网站推广建一个公司网站花多少钱
  • 最新购物网站建设框架wordpress 登录后台乱码
  • 音频网站开发做外贸网站需要营业执照
  • 企业网站搭建项目概述范文wordpress更改链接地址
  • 免费网站在线观看人数在哪直播建设工业网站首页
  • 权威的南昌网站设计wordpress游客评论游客
  • 乡镇府建设网站什么是外链
  • 营销型网站设计流程电子商务网站建设软件
  • 做个人网站的步骤wordpress 添加新页面
  • 公司建网站流程网站布局选择
  • 云南效果好的网站优化微信如何做有趣的短视频网站
  • wordpress个人网站后台登陆dedecms仿站
  • 网站没被收录什么原因上海哪家做网站
  • 电子商务网站建设含代码项目外包是什么意思
  • 此网站正在建设中页面重庆网上商城网站建设公司
  • 保定建设公司网站新产品上市的营销策划方案
  • 网站建设课程报告论文网络设计专业有前途吗
  • 苏州哪个公司做门户网站wordpress显示评论者地理位置 浏览器
  • 福州网站设计十年乐云seo推广网上国网有什么好处
  • 豪华网站建设wordpress推广提成
  • 网站优化外包价格搜索引擎费用
  • 网站建设基本范例sqlite开发网站
  • 网站建设顾问站建开发外包公司
  • 建立网站花钱吗wordpress表结构写入不全
  • 绿色家园网站怎么做导出wordpress文章
  • 合肥有什么好的网站建设公司网站建设的售后服务流程
  • 做网站烧钱吗济南免费建站
  • ps个人主页网页设计模板汕头关键词优化服务
  • 网站建设功能图网站开发新技术探索