本文主要是介绍SpringSecurity整合redission序列化问题小结(最新整理),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
《SpringSecurity整合redission序列化问题小结(最新整理)》文章详解SpringSecurity整合Redisson时的序列化问题,指出需排除官方Jackson依赖,通过自定义反序...
1. 前言
这个问题网上找了各种资料,困扰了几周,终于是解决了。记住一点,不要用SpringSecurity官方提供的jackson序列化。序列化没问题,反序列化有大坑,会报错missing type id property ‘@class’ 。
//这个就不要用了,试了,反序列化不行。 SecurityJackson2Modules.getModules(this.getClass().getClassLoader()) .forEach(objectMapper::registerModule);
2. redission配置
2.1 RedissonProperties
@Data @ConfigurationProperties(prefix = "redisson") public class RedissonProperties { /** * redis缓存key前缀 */ private String keyPrefix; /** * 线程池数量,默认值 = 当前处理核数量 * 2 */ private int threads; /** * Netty线程池数量,默认值 = 当前处理核数量 * 2 */ private int nettyThreads; /** * 单机服务配置 */ private SingleServerConfig singleServerConfig; /** * 集群服务配置 */ private ClusterServersConfig clusterServersConfig; @Data @NoArgsConstructor public static class SingleServerConfig { /** * 客户端名称 */ private String clientName; /** * 最小空闲连接数 */ private int connectionMinimumIdleSize; /** * 连接池大小 */ private int connectionPoolSize; /** * 连接空闲超时,单位:毫秒 */ private int idleConnectionTimeout; /** * 命令等待超时,单位:毫秒 */ private int timeout; /** * 发布和订阅连接池大小 【未使用,加上启动不起】 */ private int subscriptionConnectionPoolSize; } @Data @NoArgsConstructor public static class ClusterServersConfig { /** * 客户端名称 */ private String clientName; /** * master最小空闲连接数 */ private int masterConnectionMinimumIdleSize; /** * master连接池大小 */ private int masterConnectionPoolSize; /** * slave最小空闲连接数 */ private int slaveConnectionMinimumIdleSize; /** * slave连接池大小 */ private int slaveConnectionPoolSize; /** * 连接空闲超时,单位:毫秒 */ private int idleConnectionTimeout; /** * 命令等待超时,单位:毫秒 */ private int timeout; /** * 发布和订阅连接池大小 */ private int subscriptionConnectionPoolSize; /** * 读取模式 */ private ReadMode readMode; /** * 订阅模式 */ private SubscriptionMode subscriptionMode; } }
2.2 RedissionConfiguration
注意依赖排除,引用redis包排除无用依赖.
@Slf4j @EnableCaching @AutoConfiguration @EnableConfigurationProperties(RedissonProperties.class) public class RedissionConfiguration implements InitializingBean { @Resource private RedisProperties redisProperties; @Resource private RedissonProperties redissonProperties; private ObjectMapper om; @Qualifier @Bean("redisTemplate") public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); StringRedisSerializer keySerializer = new StringRedisSerializer(); RedisSerializer<Object> valueSerializer =new GenericJackson2jsonRedisSerializer( om); redisTemplate.setKeySerializer(keySerializer); redisTemplate.setValueSerializer(valueSerializer); redisTemplate.setHashKeySerializer(keySerializer); redisTemplate.setHashValueSerializer(valueSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } @Bean public Redisson redisson() { log.info("初始化redission配置..."); Config config = new Config(); config.setThreads(redissonProperties.getThreads()) .setNettyThreads(redissonProperties.getNettyThreads()) // 缓存 Lua 脚本 减少网络传输(redisson 大部分的功能都是基于 Lua 脚本实现) .setUseScriptCache(true) .setCodec(new JsonJacksonCodec(om)); RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig(); if (ObjectUtil.isNotNull(singleServerConfig)) { // 使用单机模式 SingleServerConfig singleConfig = config.useSingleServer() .setAddress("redis://" + redisProperties.getHost() + ":"+redisProperties.getPort()) .setDatabase(redisProperties.getDatabase() ) .setTimeout(singleServerConfig.getTimeout()) .setClientName(singleServerConfig.getClientName()) .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout()) .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize()) .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize()) //# DNS监测时间间隔,单位:毫秒 .setDnsMonitoringInterval(60000L); if (ObjectUtil.isNotEmpty(redisProperties.getPassword())) { singleConfig.setPassword(redisProperties.getPassword()); } } // 集群配置方式 RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig(); if (ObjectUtil.isNotNull(clusterServersConfig)) { ClusterServersConfig serversConfig = config.useClusterServers() .setTimeout(clusterServersConfig.getTimeout()) .setClientName(clusterServersConfig.getClientName()) .setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout()) .setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize()) .setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize()) .setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize()) .setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize()) .setReadMode(clusterServersConfig.getReadMode()) .setSubscriptionMode(clusterServersConfig.getSubscriptionMode()); if (ObjectUtil.isNotEmpty(redisProperties.getPassword())) { serversConfig.setPassword(redisProperties.getPassword()); } } log.info("初始化redission配置完成"); return (Redisson) Redisson.create(config); } @Override public void afterPropertiesSet() { log.info("设置objectMapper参数..."); //不影响全局objectMapper ObjectMapper copy = new ObjectMapper(); copy.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //必须设置,否则无法将JSON转化为对象,会转化成Map类型,JsonTypeInfo.As.PROPERTY序列化加@class属性 copy.activateDefaultTyping(copy.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); copy.registerSubtypes(AjUser.class, UsernamePasswordAuthenticationToken.class, SysRole.class); // 自定义ObjectMapper的时间处理模块 JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss"))); javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss"))); copy.registerModule(javaTimeModule); // 启用模块 SimpleModule module = new SimpleModule(); module.addDeserializer(AuthUser.class, new AuthUserDeserializer()); copy.registerModule(module); copy.addMixIn(AuthorizationGrantType.class,AuthorizationGrantTypeMixin.class); //自定义UnmodifiableMapMixin copy.addMixIn(Collections.unmodifiableMap(new HashMap<>()).getClass(),UnmodifiableMapMixin.class); //自定义UnmodifiableSetMixin copy.addMixIn(Collections.unmodifiableSet(new HashSet<>()).getClass(), UnmodifiableSetMixin.class); copy.addMixIn(Principal.class, PrincipalMixin.class); copy.addMixIn(UsernamePasswordAuthenticationToken.class, UsernamePasswordAuthenticationTokenMixin.class); //提前加载 copy.enable(DeserializationFeature.EAGER_DESERIALIZER_FETCH); // 忽略未知属性 copy.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); // 检查子类型 没有匹配的会报错 可调试用 //copy.disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE); // 禁用将日期序列化为时间戳的行为,解决jackson2无法反序列化LocalDateTime的问题 copy.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // 忽略非法字符 \r, \n, \t copy.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); //单独配置object赋值 om = copy; log.info("objectMapper参数设置完成..."); } }
3.自定义mixin
3.1 AuthorizationGrantTypeMixin
@JsonDeserialize(using = AuthorizationGrantTypeDeserializer.class) public abstract class AuthorizationGrantTypeMixin { @JsonCreator AuthorizationGrantTypeMixin(@JsonProperty("value") String value) { } }
3.2 PrincipalMixin
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class") public abstract class PrincipalMixin { }
3.3 UnmodifiableMapMixin
@JsonTypeInfo( use = JsonTypeInfo.Id.CLASS ) @JsonDeserialize( using = UnmodifiableMapDeserializer.class ) public class UnmodifiableMapMixin { @JsonCreator UnmodifiableMapMixin(Map<?, ?> map) { } }
3.4 UnmodifiableSetMixin
@JsonTypeInfo( use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY ) @JsonDeserialize( using = UnmodifiableSetDeserializer.class ) public abstract class UnmodifiableSetMixin { @JsonCreator UnmodifiableSetMixin(Set<?> s) { } }
3.5 UsernamePasswordAuthenticationTokenMixin
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class") @JsonDeserialize( using = UsernamePasswordAuthenticationTokenDeserializer.class ) public abstract class UsernamePasswordAuthenticationTokenMixin { @JsonCreator public UsernamePasswordAuthenticationTokenMixin( @JsonProperty("principal") Object principal, @JsonProperty("credentials") Object credentials, @JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities) { } }
4. 自定义deserializer
这里得注意几个问题:
1.为什么deserializer要用new ObjectMapper
答:用JsonParse的 jp.getCodec().readTree(jp);会报错 missing type id property ‘@class’ 。
2.为什么读取子类转化子类的时候要用jp.getCodec().treeToValue(jsonNode, clazz)
答:自定义反序列化器注册在原ObjectMapper里面的,new ObjectMapper不包含这些处理会报错 missing type id property ‘@class’ 。
3.为什么不适用SpringSecurity自带的序列化器,比如CoreJackson2Module
答:同问题1,源码里面的是
ObjectMapper mapper = (ObjectMapper)jp.getCodec();
JsonNode node = (JsonNode)mapper.readTree(jp);//运行到这行会报错
换成新ObjectMapper则不报错
4.1 AuthorizationGrantTypeDeserializer
@Slf4j public class AuthorizationGrantTypeDeserializer extends StdDeserializer<AuthorizationGrantType> { private final ObjectMapper noTypingMapper = new ObjectMapper(); public AuthorizationGrantTypeDeserializer() { super(AuthorizationGrantType.class); log.info(">> AuthorizationGrantTypeDeserializer 实例化完成 >>"); } @Override public AuthorizationGrantType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { log.info(">> Using AuthorizationGrantTypeDeserializer"); JsonNode node = noTypingMapper.readTree(p); // 支持两种格式:纯字符串 或 {"value": "client_credentials"} String value = node.isTextual() ? node.asText() : node.get("value").asText(); return switch (value) { case "authorization_code" -> AuthorizationGrantType.AUTHORIZATION_CODE; case "client_credentials" -> AuthorizationGrantType.CLIENT_CREDENTIALS; case "refresh_token" -> AuthorizationGrantType.REFRESH_TOKEN; default -> new AuthorizationGrantType(value); }; } }
4.2 UnmodifiableMapDeserializer
@Slf4j public class UnmodifiableMapDeserializer extends StdDeserializer<Map<?, ?>> { private final ObjectMapper noTypingMapper = new ObjectMapper(); public UnmodifiableMapDeserializer() { super(Map.class); log.info(">> UnmodifiableMapDeserializer 实例化完成 >>"); } @Override public Map<?, ?> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { log.info(">> Using UnmodifiableMapDeserializer"); ObjectCodec codec = jp.getCodec(); JsonNode node = noTypingMapper.readTree(jp); Map<String, Object> result = new LinkedHashMap<>(); if (node != null && node.isObject()) { Objects.requireNonNull(node); for (Iterator<Map.Entry<String, JsonNode>> it = node.fields(); it.hasNext(); ) { Map.Entry<String, JsonNode> field = it.next(); JsonNode value = field.getValue(); String key = field.getKey(); if (key.contains("Principal") && value.has("@class")) { String className = value.get("@class").asText(); try { Class<?> clazz = Class.forName(className); //Object val =noTypingMapper.readValue(value.traverse(noTypingMapper),clazz); result.put(key,codec.treeToValue(value, clazz)); } catch (Exception e) { throw new RuntimeException("无法反序列化 principal", e); } } else { // 默认处理其他字段(按 Map 反序列化) result.put(key, noTypingMapper.readValue(value.traverse(noTypingMapper), Object.class)); } } } return Collections.unmodifiableMap(result); } }
4.3 UnmodifiableSetDeserializer
@Slf4j public class UnmodifiableSetDeserializer extends StdDeserializer<Set<?>> { private final ObjectMapper noTypingMapper = new ObjectMapper(); public UnmodifiableSetDeserializer() { super(Set.class); log.info(">> UnmodifiableSetDeserializer 实例化完成 >>"); } @Override public Set<?> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { log.info(">> Using UnmodifiableSetDeserializer"); JsonNode node = noTypingMapper.readTree(jp); Set<String> result = new LinkedHashSet<>(node.size()); if (node != null && node.isObject()) { Objects.requireNonNull(node); for (JsonNode jsonNode : node) { result.add(jsonNode.asText()); } } return Collections.unmodifiableSet(result); } }
4.4 UsernamePasswordAuthenticationTokenDeserializer
@Slf4j puChina编程blic class UsernamePasswordAuthenticationTokenDeserializer extends StdDeserializer<UsernamePasswordAuthenticationToken> { private final ObjectMapper noTypingMapper = new ObjectMapper(); protected UsernamePasswordAuthenticationTokenDeserializer() { super(UsernamePasswordAuthenticationToken.class); log.info(">> UsernamePasswordAuthenticationTokenDeserializer 实例化完成 >>"); } @Override @SneakyThrows public UsernamePasswordAuthenticationToken deserialize(JsonParser jp, DeserializationContext ctxt) { log.info(">> Using UsernamePasswordAuthenticationTokenDeserializer"); JsonNode jsonNode = noTypingMapper.readTree(jp); JsonNode principalNode = this.readJsonNode(jsonNode, "principal"); Object principal = this.getPrincipal(jp, principalNode); JsonNode credentialsNode = this.readJsonNode(jsonNode, "credentials"); JsonNode authoritiesNode = this.readJsonNode(jsonNode, "authorities"); Object credentials = this.getCredentials(credentialsNode); Collection<SimpleGrantedAuthority> authorities = new ArrayList<>(); if (authoritiesNode != null && authoritiesNode.isArray() && authoritiesNode.size() == 2) { //第一个是类型,第二个才是存的值 JsonNode actualAuthoritiesArray = authoritiesNode.get(1); // 第二个元素是真实列表 if (actualAuthoritiesArray != null && actualAuthoritiesArray.isArray()) { for (JsonNode authNode : actualAuthoritiesArray) { if (!authNode.has("@class")) { String role = authNode.get("authority").asText(); SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role); authorities.add(authority); } } } } // 构造 token 对象 return new UsernamePasswordAuthenticationToken(principal, credentials, authorities); } private Object getCredentials(JsonNode credentialsNode) { return !credentialsNode.isNull() && !credentialsNode.isMissingNode() ? credentialsNode.asText() : null; } private Object getPrincipal(JsonParser jp, JsonNode principalNode) throws IOException, ClassNotFoundException { String className = principalNode.get("@class").asText(); Class<?> clazz = Class.forName(className); //使用原mapper才能使用UserDeserializer return principalNode.isObject() ? jp.getCodec().treeToValue(principalNode, clazz): principalNode.asText(); } private JsonNode readJsonNode(JsonNode jsonNode, String field) { return jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance(); } }
4.5 AuthUserDeserializer
@Slf4j public class AuthUserDeserializer extends StdDeserializer<AuthUser> { private final ObjectMapper noTypingMapper = new ObjectMapper(); public AuthUserDeserializer() { super(AuthUser.class); log.info(">> AjUserDeserializer 实例化完成 >>"); } @Override @SneakyThrows public AuthUser deserialize(JsonParser p, DeserializationContext ctxt) { log.info(">> Using AjUserDeserializer"); JsonNode jsonNode = noTypingMapper.readTree(p); JsonNode idNode = this.readJsonNode(jsonNode, "id"); JsonNode deptIdNode = this.readJsonNode(jsonNode, "deptId"); JsonNode phoneNode = this.readJsonNode(jsonNode, "phone"); JsonNode usernameNode = this.readJsonNode(jsonNode, "username"); JsonNode passwordNode = this.readJsonNode(jsonNode, "password"); JsonNode accountNonLockedNode = this.readJsonNode(jsonNode, "accountNonLocked"); //索引0是类型,1是数据 long id = Long.parseLong(idNode.get(1).asText()); long deptId = Long.parseLong(deptIdNode.get(1).asText()); String phone = phoneNode.asText(); String username= usernameNode.asText(); String password = passwordNode.asText(); boolean accountNonLocked = Boolean.parseBoolean(accountNonLockedNode.asText()); List<SimpleGrantedAuthority> authorities = new ArrayList<>(); JsonNode authoritiesNode = this.getAuthorities(jsonNode); if (authoritiesNode != null && authoritiesNode.isArray() && authoritiesNode.size() == 2) { //第一个是类型,第二个才是存的值 JsonNode actualAuthoritiesArray = authoritiesNode.get(1); // 第二个元素是http://www.chinasem.cn真实列表 if (actualAuthoritiesArray != null && actualAuthoritiesArray.isArray()) { for (JsonNode authNode : actualAuthoritiesArray) { if (!authNode.has("@class")) { String role = authNode.get("authority").asText(); SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role); authorities.add(authority); } } } } //取缓存不加SecurityConstants.BCRYPT 加密的特征码 return new AuthUser(id, deptId,username, password, phone, (SysRole) this.getSysRole(p,this.readJsonNode(jsonNode, "sysRole")),true, true, true, FasdoYvNMC accountNonLocked, authorities); } private JsonNode getAuthorities(JsonNode jsonNode) { return jsonNode.has("authorities") ? jsonNode.get("authorities") : MissingNode.getInstance(); } private Object getSysRole(JsonParser jp, JsonNode node) throws IOException, ClassNotFoundException { String className = node.get("@class").asText(); Class<?> clazz = Class.forName(className); return node.isObject() ?jp.getCodec().treeToValue(node, clazz) : node.asText(); } private JsonNode readJsonNode(JsonNode jsonNode, String field) { return jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance(); } }
5. 自定义扩展用户信息 AuthUser
@Getter public class AuthUser extends User implements OAuth2AuthenticatedPrincipal { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; /** * 用户ID */ @JsonSerialize(using = ToStringSerializer.class) private final Long id; /** * 部门ID */ @JsonSerialize(using = ToStringSerializer.class) private final Long deptId; /** * 手机号 */ private final String phone; /** * 角色 */ private final SysRole sysRole; @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) public AuthUser(@JsonProperty("id") Long id, @JsonProperty("deptId") Long deptId, @JsonProperty("username") String username, @JsonProperty("password") String password, @JsonProperty("phone") String phone, @JsonProperty("sysRole") SysRole sysRole, @JsonProperty("enabled") boolean enabled, @JsonProperty("accountNonExpired") boolean accountNonExpired, @JsonProperty("credentialsNonExpired") boolean credentialsNonExpired, @JsonProperty("accountNonLocked") boolean accountNonLocked, @JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities) { super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); this.id = id; this.deptId = deptId; this.phone = phone; this.sysRole = sysRole; } /** * Get the OAuth 2.0 token attributes * @return the OAuth 2.0 token attributes */ @Override public Map<String, Object> getAttributes() { return new HashMap<>(); } @Override public String getName() { return this.getUsername(); } }
6. SpringSecurity其他地方改动
这里就不再贴SpringSecurity其他代码了,自行实现,开源框架Pig自取
6.1 认证服务器配置
@Configuration @RequiredArgsConstructor public class AuthorizationServerConfiguration { private final OAuth2AuthorizationService authorizationService; private final PasswordDecoderFilter passwordDecoderFilter; private final ValidateCodeFilter validateCodeFilter; /** * Authorization Server 配置,仅对 /oauth2/** 的请求有效 * @param http http * @return {@link SecurityFilterChain } * @throws Exception 异常 */ @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SecurityFilterChain authorizationServer(HttpSecurity http) throws Exception { // 配置授权服务器的安全策略,只有http://www.chinasem.cn/oauth2/**的请求才会走如下的配置 http.securityMatcher("/oauth2/**"); OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer(); // 增加验证码过滤器 http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class); // 增加密码解密过滤器 http.addFilterBefore(passwordDecoderFilter, UsernamePasswordAuthenticationFilter.class); http.with(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {// 个性化认证授权端点 tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter()) // 注入自定义的授权认证Converter .accessTokenResponseHandler(new AjAuthenticationSuccessEventHandler()) // 登录成功处理器 .errorResponseHandler(new AjAuthenticationFailureEventHandler());// 登录失败处理器 }).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 个性化客户端认证 oAuth2ClientAuthenticationConfigurer.errorResponseHandler(new AjAuthenticationFailureEventHandler()))// 处理客户端认证异常 .authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint// 授权码端点个性化confirm页面 .consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI)), Customizer.withDefaults()) .authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated()); // 设置 Token 存储的策略 http.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现 .authorizationServerSettings( AuthorizationServerSettings.builder().issuer(SecurityConstants.PROJECT_LICENSE).build()), Customizer.withDefaults()); // 设置授权码模式登录页面 http.with(new FormIdentityLoginConfigurer(), Customizer.withDefaults()); DefaultSecurityFilterChain securityFilterChain = http.build(); // 注入自定义授权模式实现 addCustomOAuth2GrantAuthenticationProvider(http); return securityFilterChain; } /** * 令牌生成规则实现 </br> * client:username:uuid * @return OAuth2TokenGenerator */ @Bean public OAuth2TokenGenerator oAuth2TokenGenerator() { CustomeOAuth2AccessTokenGenerator accessTokenGenerator = new CustomeOAuth2AccessTokenGenerator(); // 注入Token 增加关联用户信息 accessTokenGenerator.setAccessTokenCustomizer(new CustomeOAuth2TokenCustomizer()); return new DelegatingOAuth2TokenGenerator(accessTokenGenerator, new OAuth2RefreshTokenGenerator()); } /** * request -> xToken 注入请求转换器 * @return DelegatingAuthenticationConverter */ @Bean public AuthenticationConverter accessTokenRequestConverter() { return new DelegatingAuthenticationConverter(Arrays.asList( new OAuth2ResourceOwnerPasswordAuthenticationConverter(), new OAuth2ResourceOwnerSmsAuthenticationConverter(), new OAuth2RefreshTokenAuthenticationConverter(), new OAuth2ClientCredentialsAuthenticationConverter(), new OAuth2AuthorizationCodeAuthenticationConverter(), new OAuth2AuthorizationCodeRequestAuthenticationConverter())); } /** * 注入授权模式实php现提供方 * <p> * 1. 密码模式 </br> * 2. 短信登录 </br> */ private void addCustomOAuth2GrantAuthenticationProvider(HttpSecurity http) { AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class); OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class); OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider( authenticationManager, authorizationService, oAuth2TokenGenerator()); OAuth2ResourceOwnerSmsAuthenticationProvider resourceOwnerSmsAuthenticationProvider = new OAuth2ResourceOwnerSmsAuthenticationProvider( authenticationManager, authorizationService, oAuth2TokenGenerator()); // 处理 UsernamePasswordAuthenticationToken http.authenticationProvider(new DaoAuthenticationProvider()); // 处理 OAuth2ResourceOwnerPasswordAuthenticationToken http.authenticationProvider(resourceOwnerPasswordAuthenticationProvider); // 处理 OAuth2ResourceOwnerSmsAuthenticationToken http.authenticationProvider(resourceOwnerSmsAuthenticationProvider); } }
到此这篇关于SpringSecurity整合redission序列化问题的文章就介绍到这了,更多相关SpringSecurity整合redission序列化内容请搜索编程China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!
这篇关于SpringSecurity整合redission序列化问题小结(最新整理)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!