SpringSecurity整合redission序列化問題小結(jié)(最新整理)
1. 前言
這個問題網(wǎng)上找了各種資料,困擾了幾周,終于是解決了。記住一點,不要用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;
/**
* 線程池數(shù)量,默認值 = 當前處理核數(shù)量 * 2
*/
private int threads;
/**
* Netty線程池數(shù)量,默認值 = 當前處理核數(shù)量 * 2
*/
private int nettyThreads;
/**
* 單機服務(wù)配置
*/
private SingleServerConfig singleServerConfig;
/**
* 集群服務(wù)配置
*/
private ClusterServersConfig clusterServersConfig;
@Data
@NoArgsConstructor
public static class SingleServerConfig {
/**
* 客戶端名稱
*/
private String clientName;
/**
* 最小空閑連接數(shù)
*/
private int connectionMinimumIdleSize;
/**
* 連接池大小
*/
private int connectionPoolSize;
/**
* 連接空閑超時,單位:毫秒
*/
private int idleConnectionTimeout;
/**
* 命令等待超時,單位:毫秒
*/
private int timeout;
/**
* 發(fā)布和訂閱連接池大小 【未使用,加上啟動不起】
*/
private int subscriptionConnectionPoolSize;
}
@Data
@NoArgsConstructor
public static class ClusterServersConfig {
/**
* 客戶端名稱
*/
private String clientName;
/**
* master最小空閑連接數(shù)
*/
private int masterConnectionMinimumIdleSize;
/**
* master連接池大小
*/
private int masterConnectionPoolSize;
/**
* slave最小空閑連接數(shù)
*/
private int slaveConnectionMinimumIdleSize;
/**
* slave連接池大小
*/
private int slaveConnectionPoolSize;
/**
* 連接空閑超時,單位:毫秒
*/
private int idleConnectionTimeout;
/**
* 命令等待超時,單位:毫秒
*/
private int timeout;
/**
* 發(fā)布和訂閱連接池大小
*/
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 腳本 減少網(wǎng)絡(luò)傳輸(redisson 大部分的功能都是基于 Lua 腳本實現(xiàn))
.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監(jiān)測時間間隔,單位:毫秒
.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("設(shè)置objectMapper參數(shù)...");
//不影響全局objectMapper
ObjectMapper copy = new ObjectMapper();
copy.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//必須設(shè)置,否則無法將JSON轉(zhuǎn)化為對象,會轉(zhuǎn)化成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);
// 檢查子類型 沒有匹配的會報錯 可調(diào)試用
//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參數(shù)設(shè)置完成...");
}
}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.為什么讀取子類轉(zhuǎn)化子類的時候要用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
public 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);
}
}
}
}
// 構(gòu)造 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是數(shù)據(jù)
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); // 第二個元素是真實列表
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,
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其他代碼了,自行實現(xiàn),開源框架Pig自取
6.1 認證服務(wù)器配置
@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 {
// 配置授權(quán)服務(wù)器的安全策略,只有/oauth2/**的請求才會走如下的配置
http.securityMatcher("/oauth2/**");
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();
// 增加驗證碼過濾器
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);
// 增加密碼解密過濾器
http.addFilterBefore(passwordDecoderFilter, UsernamePasswordAuthenticationFilter.class);
http.with(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {// 個性化認證授權(quán)端點
tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter()) // 注入自定義的授權(quán)認證Converter
.accessTokenResponseHandler(new AjAuthenticationSuccessEventHandler()) // 登錄成功處理器
.errorResponseHandler(new AjAuthenticationFailureEventHandler());// 登錄失敗處理器
}).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 個性化客戶端認證
oAuth2ClientAuthenticationConfigurer.errorResponseHandler(new AjAuthenticationFailureEventHandler()))// 處理客戶端認證異常
.authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint// 授權(quán)碼端點個性化confirm頁面
.consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI)), Customizer.withDefaults())
.authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated());
// 設(shè)置 Token 存儲的策略
http.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存儲token的實現(xiàn)
.authorizationServerSettings(
AuthorizationServerSettings.builder().issuer(SecurityConstants.PROJECT_LICENSE).build()),
Customizer.withDefaults());
// 設(shè)置授權(quán)碼模式登錄頁面
http.with(new FormIdentityLoginConfigurer(), Customizer.withDefaults());
DefaultSecurityFilterChain securityFilterChain = http.build();
// 注入自定義授權(quán)模式實現(xiàn)
addCustomOAuth2GrantAuthenticationProvider(http);
return securityFilterChain;
}
/**
* 令牌生成規(guī)則實現(xiàn) </br>
* client:username:uuid
* @return OAuth2TokenGenerator
*/
@Bean
public OAuth2TokenGenerator oAuth2TokenGenerator() {
CustomeOAuth2AccessTokenGenerator accessTokenGenerator = new CustomeOAuth2AccessTokenGenerator();
// 注入Token 增加關(guān)聯(lián)用戶信息
accessTokenGenerator.setAccessTokenCustomizer(new CustomeOAuth2TokenCustomizer());
return new DelegatingOAuth2TokenGenerator(accessTokenGenerator, new OAuth2RefreshTokenGenerator());
}
/**
* request -> xToken 注入請求轉(zhuǎn)換器
* @return DelegatingAuthenticationConverter
*/
@Bean
public AuthenticationConverter accessTokenRequestConverter() {
return new DelegatingAuthenticationConverter(Arrays.asList(
new OAuth2ResourceOwnerPasswordAuthenticationConverter(),
new OAuth2ResourceOwnerSmsAuthenticationConverter(), new OAuth2RefreshTokenAuthenticationConverter(),
new OAuth2ClientCredentialsAuthenticationConverter(),
new OAuth2AuthorizationCodeAuthenticationConverter(),
new OAuth2AuthorizationCodeRequestAuthenticationConverter()));
}
/**
* 注入授權(quán)模式實現(xiàn)提供方
* <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);
}
}到此這篇關(guān)于SpringSecurity整合redission序列化問題的文章就介紹到這了,更多相關(guān)SpringSecurity整合redission序列化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot項目啟動指定對應(yīng)環(huán)境的方法
這篇文章主要介紹了springboot項目啟動指定對應(yīng)環(huán)境的方法,本文給大家介紹的非常詳細,對大家的學(xué)習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-08-08
BeanUtils.copyProperties復(fù)制不生效的解決
這篇文章主要介紹了BeanUtils.copyProperties復(fù)制不生效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
Mybatis order by 動態(tài)傳參出現(xiàn)的問題及解決方法
今天,我正在愉快地CRUD,突然發(fā)現(xiàn)出現(xiàn)一個Bug,我們來看看是怎么回事吧!接下來通過本文給大家介紹Mybatis order by 動態(tài)傳參出現(xiàn)的一個小bug,需要的朋友可以參考下2021-07-07
完美解決Server?returned?HTTP?response?code:403?for?URL報錯問題
在調(diào)用某個接口的時候,突然就遇到了Server?returned?HTTP?response?code:?403?for?URL報錯這個報錯,導(dǎo)致獲取不到接口的數(shù)據(jù),下面小編給大家分享解決Server?returned?HTTP?response?code:403?for?URL報錯問題,感興趣的朋友一起看看吧2023-03-03
基于springmvc之常用注解,操作傳入?yún)?shù)
這篇文章主要介紹了springmvc之常用注解,操作傳入?yún)?shù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
使用Runnable實現(xiàn)數(shù)據(jù)共享
這篇文章主要為大家詳細介紹了如何使用Runnable實現(xiàn)數(shù)據(jù)共享,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-07-07

