欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringSecurity整合redission序列化問題小結(jié)(最新整理)

 更新時間:2025年07月12日 13:49:19   作者:W-同學(xué)  
文章詳解SpringSecurity整合Redisson時的序列化問題,指出需排除官方Jackson依賴,通過自定義反序列化器(如AuthorizationGrantTypeDeserializer等)和Mixin類解決反序列化報錯MissingTypeIdProperty,同時擴展用戶信息為AuthUser,并提及開源框架Pig的使用

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ù)量,默認(rèn)值 = 當(dāng)前處理核數(shù)量 * 2
     */
    private int threads;
    /**
     * Netty線程池數(shù)量,默認(rèn)值 = 當(dāng)前處理核數(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 {
					// 默認(rèn)處理其他字段(按 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 認(rèn)證服務(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) -> {// 個性化認(rèn)證授權(quán)端點
							tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter()) // 注入自定義的授權(quán)認(rèn)證Converter
									.accessTokenResponseHandler(new AjAuthenticationSuccessEventHandler()) // 登錄成功處理器
									.errorResponseHandler(new AjAuthenticationFailureEventHandler());// 登錄失敗處理器
						}).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 個性化客戶端認(rèn)證
								oAuth2ClientAuthenticationConfigurer.errorResponseHandler(new AjAuthenticationFailureEventHandler()))// 處理客戶端認(rèn)證異常
						.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)境的方法

    這篇文章主要介紹了springboot項目啟動指定對應(yīng)環(huán)境的方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-08-08
  • BeanUtils.copyProperties復(fù)制不生效的解決

    BeanUtils.copyProperties復(fù)制不生效的解決

    這篇文章主要介紹了BeanUtils.copyProperties復(fù)制不生效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Java MyBatis可視化代碼生成工具使用教程

    Java MyBatis可視化代碼生成工具使用教程

    這篇文章主要介紹了Java MyBatis可視化代碼生成工具使用教程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-11-11
  • JAVA流控及超流控后的延遲處理實例

    JAVA流控及超流控后的延遲處理實例

    這篇文章主要介紹了JAVA流控及超流控后的延遲處理,以實例形式較為詳細(xì)的分析了Java進行流量控制的技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2014-12-12
  • Java利用IO流實現(xiàn)簡易的記事本功能

    Java利用IO流實現(xiàn)簡易的記事本功能

    本文將利用Java中IO流編寫一個模擬日記本的程序,通過在控制臺輸入指令,實現(xiàn)在本地新建文件,打開日記本和修改日記本等功能,感興趣的可以了解一下
    2022-05-05
  • Mybatis order by 動態(tài)傳參出現(xiàn)的問題及解決方法

    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
  • Java多線程的具體介紹與使用筆記小結(jié)

    Java多線程的具體介紹與使用筆記小結(jié)

    Java多線程詳細(xì)介紹線程是程序執(zhí)行的最小單元,多線程是指程序同一時間可以有多個執(zhí)行單元運行(這個與你的CPU核心有關(guān))。 接下來通過本文給大家介紹Java多線程的具體介紹與使用筆記小結(jié),感興趣的朋友一起看看吧
    2021-05-05
  • 完美解決Server?returned?HTTP?response?code:403?for?URL報錯問題

    完美解決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ù)

    這篇文章主要介紹了springmvc之常用注解,操作傳入?yún)?shù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 使用Runnable實現(xiàn)數(shù)據(jù)共享

    使用Runnable實現(xiàn)數(shù)據(jù)共享

    這篇文章主要為大家詳細(xì)介紹了如何使用Runnable實現(xiàn)數(shù)據(jù)共享,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-07-07

最新評論