本文主要是介绍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序列化问题小结(最新整理)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!