配置redis相关
1. 配置Redis
package com. zzhua. blog. config. redis ;
import com. fasterxml. jackson. annotation. JsonTypeInfo ;
import com. fasterxml. jackson. core. JsonParser ;
import com. fasterxml. jackson. core. JsonProcessingException ;
import com. fasterxml. jackson. core. TreeNode ;
import com. fasterxml. jackson. databind. * ;
import com. fasterxml. jackson. databind. module. SimpleModule ;
import com. fasterxml. jackson. databind. type. TypeFactory ;
import com. fasterxml. jackson. datatype. jdk8. Jdk8Module ;
import com. fasterxml. jackson. datatype. jsr310. JavaTimeModule ;
import com. fasterxml. jackson. datatype. jsr310. deser. LocalDateTimeDeserializer ;
import com. jayway. jsonpath. spi. json. JacksonJsonNodeJsonProvider ;
import com. zzhua. blog. config. security. UserDetailDTO ;
import com. zzhua. blog. config. security. session. SessionInformationMixin ;
import com. zzhua. blog. util. JsonUtil ;
import com. zzhua. blog. util. ser. DateJsonDeserializer ;
import com. zzhua. blog. util. ser. DateJsonSerializer ;
import org. junit. Test ;
import org. springframework. context. annotation. Bean ;
import org. springframework. context. annotation. Configuration ;
import org. springframework. core. io. ClassPathResource ;
import org. springframework. data. redis. connection. RedisConnectionFactory ;
import org. springframework. data. redis. connection. StringRedisConnection ;
import org. springframework. data. redis. core. RedisTemplate ;
import org. springframework. data. redis. core. StringRedisTemplate ;
import org. springframework. data. redis. serializer. GenericJackson2JsonRedisSerializer ;
import org. springframework. data. redis. serializer. Jackson2JsonRedisSerializer ;
import org. springframework. data. redis. serializer. RedisSerializer ;
import org. springframework. security. core. session. SessionInformation ;
import springfox. documentation. spring. web. json. Json ;
import java. io. * ;
import java. nio. charset. StandardCharsets ;
import java. time. LocalDateTime ;
import java. time. format. DateTimeFormatter ;
import java. util. Arrays ;
import java. util. Calendar ;
import java. util. Date ;
import java. util. TimeZone ;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate < String , Object > redisTemplate ( RedisConnectionFactory redisConnectionFactory) {
RedisTemplate < String , Object > redisTemplate = new RedisTemplate < > ( ) ;
redisTemplate. setConnectionFactory ( redisConnectionFactory) ;
RedisSerializer < String > stringRedisSerializer = RedisSerializer . string ( ) ;
Jackson2JsonRedisSerializer < Object > jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer < > ( Object . class ) ;
ObjectMapper mapper = new ObjectMapper ( ) ;
mapper. enableDefaultTyping ( ObjectMapper. DefaultTyping . NON_FINAL, JsonTypeInfo. As . PROPERTY) ;
mapper. disable ( DeserializationFeature . FAIL_ON_UNKNOWN_PROPERTIES) ;
mapper. addMixIn ( SessionInformation . class , SessionInformationMixin . class ) ;
mapper. disable ( SerializationFeature . WRITE_DATES_AS_TIMESTAMPS) ;
mapper. setTimeZone ( TimeZone . getDefault ( ) ) ;
jackson2JsonRedisSerializer. setObjectMapper ( mapper) ;
redisTemplate. setKeySerializer ( stringRedisSerializer) ;
redisTemplate. setValueSerializer ( jackson2JsonRedisSerializer) ;
redisTemplate. setHashKeySerializer ( stringRedisSerializer) ;
redisTemplate. setHashValueSerializer ( jackson2JsonRedisSerializer) ;
return redisTemplate;
}
@Bean
public StringRedisTemplate stringRedisTemplate ( RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate ( redisConnectionFactory) ;
stringRedisTemplate. setKeySerializer ( RedisSerializer . string ( ) ) ;
stringRedisTemplate. setValueSerializer ( RedisSerializer . string ( ) ) ;
stringRedisTemplate. setHashKeySerializer ( RedisSerializer . string ( ) ) ;
stringRedisTemplate. setHashValueSerializer ( RedisSerializer . string ( ) ) ;
return stringRedisTemplate;
}
}
2. RedisService工具
package com. zzhua. blog. config. redis ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. data. redis. core. RedisTemplate ;
import org. springframework. data. redis. core. StringRedisTemplate ;
import org. springframework. stereotype. Component ;
import java. util. Collection ;
import java. util. Set ;
import java. util. concurrent. TimeUnit ;
@Component
public class RedisService {
@Autowired
private RedisTemplate < String , Object > redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
public Boolean existKey ( String key) {
return redisTemplate. hasKey ( key) ;
}
public Boolean expireKey ( String key, long timeInSeconds) {
return redisTemplate. expire ( key, timeInSeconds, TimeUnit . SECONDS) ;
}
public Boolean removeKey ( String key) {
return redisTemplate. delete ( key) ;
}
public Boolean removeKeys ( Collection < String > keys) {
return redisTemplate. delete ( keys) > 0 ;
}
public Long incr ( String key) {
return redisTemplate. opsForValue ( ) . increment ( key) ;
}
public Long incr ( String key, long delta) {
return redisTemplate. opsForValue ( ) . increment ( key, delta) ;
}
public Set < String > getKeys ( String pattern) {
return redisTemplate. keys ( pattern) ;
}
public void set ( String key, Object obj, long timeInSeconds) {
redisTemplate. opsForValue ( ) . set ( key, obj, timeInSeconds, TimeUnit . SECONDS) ;
}
public void set ( String key, Object obj) {
redisTemplate. opsForValue ( ) . set ( key, obj) ;
}
public < T > T get ( String key) {
return ( T ) redisTemplate. opsForValue ( ) . get ( key) ;
}
public void setContent ( String key, String content, long timeInSeconds) {
stringRedisTemplate. opsForValue ( ) . set ( key, content, timeInSeconds, TimeUnit . SECONDS) ;
}
public void setContent ( String key, String content) {
stringRedisTemplate. opsForValue ( ) . set ( key, content) ;
}
public String getContent ( String key) {
return stringRedisTemplate. opsForValue ( ) . get ( key) ;
}
}
3. RedisSessionRegistry会话注册中心
package com. zzhua. blog. config. security. session ;
import com. zzhua. blog. config. redis. RedisConstants ;
import com. zzhua. blog. config. redis. RedisService ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. context. ApplicationListener ;
import org. springframework. security. core. session. SessionInformation ;
import org. springframework. security. core. session. SessionRegistry ;
import org. springframework. stereotype. Component ;
import org. springframework. util. CollectionUtils ;
import java. util. * ;
import java. util. stream. Collectors ;
@Component
public class RedisSessionRegistry implements SessionRegistry , ApplicationListener < RedisSessionDestroyedEvent > {
@Autowired
private RedisService redisService;
@Override
public List < Object > getAllPrincipals ( ) {
Map < Object , List < SessionInformation > > principalSessions = getPrincipalSessions ( ) ;
return Arrays . asList ( principalSessions. keySet ( ) . toArray ( ) ) ;
}
@Override
public List < SessionInformation > getAllSessions ( Object principal, boolean includeExpiredSessions) {
Map < Object , List < SessionInformation > > principalSessions = getPrincipalSessions ( ) ;
List < SessionInformation > sessionInformations = principalSessions. get ( principal) ;
if ( ! CollectionUtils . isEmpty ( sessionInformations) ) {
List < SessionInformation > siList = new ArrayList < > ( ) ;
for ( SessionInformation sessionInformation : sessionInformations) {
if ( includeExpiredSessions || ! sessionInformation. isExpired ( ) ) {
siList. add ( sessionInformation) ;
}
}
return siList;
}
return Collections . emptyList ( ) ;
}
private Map < Object , List < SessionInformation > > getPrincipalSessions ( ) {
Set < String > keys = redisService. getKeys ( RedisConstants . SECURITY_SESSION_KEY_PREFIX + "*" ) ;
if ( ! CollectionUtils . isEmpty ( keys) ) {
List < SessionInformation > sessionInformations = new ArrayList < > ( ) ;
for ( String key : keys) {
SessionInformation sessionInformation = redisService. get ( key) ;
sessionInformations. add ( sessionInformation) ;
}
Map < Object , List < SessionInformation > > principals = sessionInformations. stream ( ) . collect ( Collectors . groupingBy ( SessionInformation :: getPrincipal ) ) ;
return principals;
}
return Collections . emptyMap ( ) ;
}
@Override
public SessionInformation getSessionInformation ( String sessionId) {
return redisService. get ( getKey ( sessionId) ) ;
}
@Override
public void refreshLastRequest ( String sessionId) {
SessionInformation sessionInformation = getSessionInformation ( sessionId) ;
sessionInformation. refreshLastRequest ( ) ;
redisService. set ( getKey ( sessionId) , sessionInformation) ;
}
@Override
public void registerNewSession ( String sessionId, Object principal) {
SessionInformation sessionInformation = new SessionInformation ( principal, sessionId, new Date ( ) ) ;
redisService. set ( getKey ( sessionId) , sessionInformation, RedisConstants . LOGIN_VALID_TIMING) ;
}
@Override
public void removeSessionInformation ( String sessionId) {
redisService. removeKey ( getKey ( sessionId) ) ;
}
@Override
public void onApplicationEvent ( RedisSessionDestroyedEvent event) {
redisService. removeKey ( getKey ( event. getSessionId ( ) ) ) ;
}
private String getKey ( String sessionId) {
return RedisConstants . SECURITY_SESSION_KEY_PREFIX + sessionId;
}
}
4. SessionInformationMixin解决反序列化问题
package com. zzhua. blog. config. security. session ;
import com. fasterxml. jackson. annotation. JsonCreator ;
import com. fasterxml. jackson. annotation. JsonProperty ;
import java. util. Date ;
public abstract class SessionInformationMixin {
@JsonCreator
public SessionInformationMixin ( @JsonProperty ( "principal" ) Object principal,
@JsonProperty ( "sessionId" ) String sessionId,
@JsonProperty ( "lastRequest" ) Date lastRequest) {
}
}
5. TokenSessionAuthenticationStrategy
package com. zzhua. blog. config. security. session ;
import com. zzhua. blog. config. ex. BizException ;
import com. zzhua. blog. config. redis. RedisConstants ;
import com. zzhua. blog. config. redis. RedisService ;
import com. zzhua. blog. util. JsonUtil ;
import com. zzhua. blog. util. Result ;
import com. zzhua. blog. util. ResultCodeEnum ;
import lombok. extern. slf4j. Slf4j ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. beans. factory. annotation. Value ;
import org. springframework. security. core. Authentication ;
import org. springframework. security. core. session. SessionInformation ;
import org. springframework. security. web. authentication. session. SessionAuthenticationException ;
import org. springframework. security. web. authentication. session. SessionAuthenticationStrategy ;
import org. springframework. stereotype. Component ;
import javax. servlet. http. HttpServletRequest ;
import javax. servlet. http. HttpServletResponse ;
import java. util. Comparator ;
import java. util. List ;
import java. util. UUID;
@Slf4j
@Component
public class TokenSessionAuthenticationStrategy implements SessionAuthenticationStrategy {
@Value ( "${token.maxiumSessions:2}" )
private int maxiumSessions;
@Value ( "${token.maxiumSessionsPreventsLogin:false}" )
private boolean maxiumSessionsPreventsLogin;
@Autowired
private RedisService redisService;
@Autowired
private RedisSessionRegistry redisSessionRegistry;
@Override
public void onAuthentication ( Authentication authentication, HttpServletRequest request, HttpServletResponse response) throws SessionAuthenticationException {
validateMaximumSessions ( authentication, response) ;
registerNewSession ( authentication, response) ;
}
private void validateMaximumSessions ( Authentication authentication, HttpServletResponse response) {
List < SessionInformation > princinpalSessions = redisSessionRegistry. getAllSessions ( authentication. getPrincipal ( ) , false ) ;
int sessionCount = princinpalSessions. size ( ) ;
int maxiumSessions = getMaximumSessionsForThisUser ( authentication) ;
if ( sessionCount < maxiumSessions) {
return ;
}
if ( maxiumSessions == - 1 ) {
return ;
}
if ( maxiumSessionsPreventsLogin) {
log. error ( "会话数量超过限制: {}" , JsonUtil . obj2Json ( authentication. getPrincipal ( ) ) ) ;
throw BizException . MAXIMUM_SESSION_EXCEED_ERR;
}
princinpalSessions. sort ( Comparator . comparing ( SessionInformation :: getLastRequest ) ) ;
int maximumSessionsExceededBy = princinpalSessions. size ( ) - maxiumSessions + 1 ;
List < SessionInformation > sessionsToBeExpired = princinpalSessions. subList ( 0 , maximumSessionsExceededBy) ;
for ( SessionInformation session : sessionsToBeExpired) {
session. expireNow ( ) ;
redisService. set ( RedisConstants . SECURITY_SESSION_KEY_PREFIX + session. getSessionId ( ) , session) ;
}
}
private void registerNewSession ( Authentication authentication, HttpServletResponse response) {
String token = UUID. randomUUID ( ) . toString ( ) . replace ( "-" , "" ) ;
redisSessionRegistry. registerNewSession ( token, authentication. getPrincipal ( ) ) ;
JsonUtil . writeJsonToResponse ( Result . ok ( token) , response) ;
}
private int getMaximumSessionsForThisUser ( Authentication authentication) {
return maxiumSessions;
}
}
6. RedisSessionSecurityContextRepository
package com. zzhua. blog. config. security. session ;
import com. zzhua. blog. config. redis. RedisConstants ;
import com. zzhua. blog. config. redis. RedisService ;
import com. zzhua. blog. config. security. UserDetailDTO ;
import com. zzhua. blog. config. security. session. RedisSessionRegistry ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. security. authentication. AnonymousAuthenticationToken ;
import org. springframework. security. authentication. UsernamePasswordAuthenticationToken ;
import org. springframework. security. core. Authentication ;
import org. springframework. security. core. context. SecurityContext ;
import org. springframework. security. core. context. SecurityContextHolder ;
import org. springframework. security. core. session. SessionInformation ;
import org. springframework. security. core. session. SessionRegistry ;
import org. springframework. security. web. context. HttpRequestResponseHolder ;
import org. springframework. security. web. context. SecurityContextRepository ;
import org. springframework. stereotype. Component ;
import org. springframework. util. StringUtils ;
import javax. servlet. http. HttpServletRequest ;
import javax. servlet. http. HttpServletResponse ;
@Component
public class RedisSessionSecurityContextRepository implements SecurityContextRepository {
private static final String AUTHENTICATION_IN_SECURITY_CONTEXT_KEY = "AUTHENTICATION_IN_REDIS_SECURITY_CONTEXT" ;
@Autowired
private RedisService redisService;
@Override
public SecurityContext loadContext ( HttpRequestResponseHolder requestResponseHolder) {
SecurityContext securityContext = SecurityContextHolder . createEmptyContext ( ) ;
HttpServletRequest request = requestResponseHolder. getRequest ( ) ;
try {
String authorization = request. getHeader ( RedisConstants . AUTHORIZATIO_HEADER) ;
if ( authorization != null && redisService. existKey ( getKey ( authorization) ) ) {
SessionInformation sessionInformation = redisService. get ( getKey ( authorization) ) ;
if ( sessionInformation != null ) {
UserDetailDTO userDetailDTO = ( UserDetailDTO ) sessionInformation. getPrincipal ( ) ;
UsernamePasswordAuthenticationToken upAuthToken = new UsernamePasswordAuthenticationToken ( userDetailDTO, "" , userDetailDTO. getAuthorities ( ) ) ;
securityContext. setAuthentication ( upAuthToken) ;
RequestedSessionInfomationHolder . set ( sessionInformation) ;
if ( ! sessionInformation. isExpired ( ) ) {
redisService. expireKey ( getKey ( sessionInformation. getSessionId ( ) ) , RedisConstants . LOGIN_VALID_TIMING) ;
}
}
}
} catch ( Exception e) {
}
request. setAttribute ( AUTHENTICATION_IN_SECURITY_CONTEXT_KEY, securityContext. getAuthentication ( ) ) ;
return securityContext;
}
@Override
public void saveContext ( SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
Authentication authenticationBefore = ( Authentication ) request. getAttribute ( AUTHENTICATION_IN_SECURITY_CONTEXT_KEY) ;
Authentication authenticationAfter = context. getAuthentication ( ) ;
if ( authenticationAfter == null || AnonymousAuthenticationToken . class . isAssignableFrom ( authenticationAfter. getClass ( ) ) ) {
String authorizationHeader = request. getHeader ( RedisConstants . AUTHORIZATIO_HEADER) ;
if ( authenticationBefore != null && redisService. existKey ( getKey ( authorizationHeader) ) ) {
redisService. removeKey ( getKey ( authorizationHeader) ) ;
}
return ;
}
if ( authenticationAfter != null && authenticationAfter != authenticationBefore) {
String authorizationHeader = request. getHeader ( RedisConstants . AUTHORIZATIO_HEADER) ;
if ( ! StringUtils . isEmpty ( authorizationHeader) && authenticationBefore != null ) {
redisService. set ( getKey ( authorizationHeader) , authenticationAfter. getPrincipal ( ) ) ;
}
}
}
@Override
public boolean containsContext ( HttpServletRequest request) {
String authorization = request. getHeader ( RedisConstants . AUTHORIZATIO_HEADER) ;
return ! StringUtils . isEmpty ( authorization) && redisService. existKey ( getKey ( authorization) ) ;
}
private String getKey ( String sessionId) {
return RedisConstants . SECURITY_SESSION_KEY_PREFIX + sessionId;
}
}
7. RequestedSessionInfomationHolder
public class RequestedSessionInfomationHolder {
private static final ThreadLocal < SessionInformation > SESSION_INFO_HOLDER = new ThreadLocal < > ( ) ;
public static SessionInformation get ( ) {
return SESSION_INFO_HOLDER. get ( ) ;
}
public static void set ( SessionInformation sessionInformation) {
SESSION_INFO_HOLDER. set ( sessionInformation) ;
}
public static void clear ( ) {
SESSION_INFO_HOLDER. remove ( ) ;
}
}
8. RedisConcurrentSessionFilter
package com. zzhua. blog. config. security. session ;
import com. zzhua. blog. config. redis. RedisConstants ;
import com. zzhua. blog. config. redis. RedisService ;
import com. zzhua. blog. config. security. SysLogoutHandler ;
import com. zzhua. blog. util. JsonUtil ;
import com. zzhua. blog. util. Result ;
import com. zzhua. blog. util. ResultCodeEnum ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. security. core. context. SecurityContextHolder ;
import org. springframework. security. core. session. SessionInformation ;
import org. springframework. security. core. session. SessionRegistry ;
import org. springframework. security. web. authentication. logout. LogoutHandler ;
import org. springframework. stereotype. Component ;
import org. springframework. web. filter. OncePerRequestFilter ;
import javax. servlet. FilterChain ;
import javax. servlet. ServletException ;
import javax. servlet. http. HttpServletRequest ;
import javax. servlet. http. HttpServletResponse ;
import java. io. IOException ;
public class RedisConcurrentSessionFilter extends OncePerRequestFilter {
@Autowired
private RedisSessionRegistry sessionRegistry;
@Autowired
private RedisService redisService;
@Autowired
private SysLogoutHandler sysLogoutHandler;
@Override
protected void doFilterInternal ( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException , IOException {
SessionInformation info = RequestedSessionInfomationHolder . get ( ) ;
if ( info != null ) {
if ( info. isExpired ( ) ) {
sysLogoutHandler. logout ( request, response, SecurityContextHolder . getContext ( ) . getAuthentication ( ) ) ;
JsonUtil . writeJsonToResponse ( Result . fail ( ResultCodeEnum . SESSION_KEY_EXPIRED) , response) ;
return ;
}
this . sessionRegistry. refreshLastRequest ( info. getSessionId ( ) ) ;
}
filterChain. doFilter ( request, response) ;
}
}
UserDetailDTO
package com. zzhua. blog. config. security ;
import com. fasterxml. jackson. annotation. JsonIgnore ;
import lombok. Data ;
import org. springframework. security. core. GrantedAuthority ;
import org. springframework. security. core. authority. AuthorityUtils ;
import org. springframework. security. core. authority. SimpleGrantedAuthority ;
import org. springframework. security. core. userdetails. UserDetails ;
import org. springframework. util. CollectionUtils ;
import java. util. * ;
import java. util. stream. Collectors ;
@Data
public class UserDetailDTO implements UserDetails {
private Integer userAuthId;
private Integer userInfoId;
private String nickname;
private String avatar;
private String bio;
private String website;
private Integer loginType;
private String username;
private Date loginTime;
private List < String > perms;
private List < String > roles;
@JsonIgnore
private String password;
private Integer disabled;
@Override
@JsonIgnore
public Collection < ? extends GrantedAuthority > getAuthorities ( ) {
if ( ! CollectionUtils . isEmpty ( this . perms) || ! CollectionUtils . isEmpty ( this . roles) ) {
ArrayList < GrantedAuthority > list = new ArrayList < > ( ) ;
List < GrantedAuthority > permAuthorities = roles. stream ( ) . map ( role -> new SimpleGrantedAuthority ( "ROLE_" + role) ) . collect ( Collectors . toList ( ) ) ;
List < GrantedAuthority > rolePermAuthories = AuthorityUtils . createAuthorityList ( perms. toArray ( new String [ 0 ] ) ) ;
list. addAll ( permAuthorities) ;
list. addAll ( rolePermAuthories) ;
return list;
} else {
return Collections . emptyList ( ) ;
}
}
@Override
public String getPassword ( ) {
return this . password;
}
@Override
public String getUsername ( ) {
return this . username;
}
@Override
@JsonIgnore
public boolean isAccountNonExpired ( ) {
return true ;
}
@Override
@JsonIgnore
public boolean isAccountNonLocked ( ) {
return true ;
}
@Override
@JsonIgnore
public boolean isCredentialsNonExpired ( ) {
return true ;
}
@Override
@JsonIgnore
public boolean isEnabled ( ) {
return disabled != null && disabled == 0 ;
}
@Override
public boolean equals ( Object o) {
if ( this == o) return true ;
if ( o == null || getClass ( ) != o. getClass ( ) ) return false ;
UserDetailDTO that = ( UserDetailDTO ) o;
return Objects . equals ( userInfoId, that. userInfoId) ;
}
@Override
public int hashCode ( ) {
return Objects . hash ( userInfoId) ;
}
}
配置security
1. SecurityConfig
package com. zzhua. blog. config. security ;
import com. zzhua. blog. config. security. session. RedisConcurrentSessionFilter ;
import com. zzhua. blog. config. security. session. RedisSessionRegistry ;
import com. zzhua. blog. config. security. session. RedisSessionSecurityContextRepository ;
import com. zzhua. blog. config. security. session. TokenSessionAuthenticationStrategy ;
import com. zzhua. blog. util. ApplicationContextUtil ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. context. annotation. Bean ;
import org. springframework. context. annotation. Configuration ;
import org. springframework. security. config. annotation. ObjectPostProcessor ;
import org. springframework. security. config. annotation. method. configuration. EnableGlobalMethodSecurity ;
import org. springframework. security. config. annotation. web. builders. HttpSecurity ;
import org. springframework. security. config. annotation. web. builders. WebSecurity ;
import org. springframework. security. config. annotation. web. configuration. EnableWebSecurity ;
import org. springframework. security. config. annotation. web. configuration. WebSecurityConfigurerAdapter ;
import org. springframework. security. config. http. SessionCreationPolicy ;
import org. springframework. security. core. Authentication ;
import org. springframework. security. crypto. bcrypt. BCryptPasswordEncoder ;
import org. springframework. security. crypto. password. PasswordEncoder ;
import org. springframework. security. web. AuthenticationEntryPoint ;
import org. springframework. security. web. authentication. AuthenticationFailureHandler ;
import org. springframework. security. web. authentication. AuthenticationSuccessHandler ;
import org. springframework. security. web. authentication. session. CompositeSessionAuthenticationStrategy ;
import org. springframework. security. web. authentication. session. RegisterSessionAuthenticationStrategy ;
import org. springframework. security. web. authentication. session. SessionAuthenticationStrategy ;
import org. springframework. security. web. session. SessionManagementFilter ;
import org. springframework. web. cors. CorsConfiguration ;
import javax. servlet. http. HttpServletRequest ;
import javax. servlet. http. HttpServletResponse ;
import java. util. Arrays ;
import java. util. List ;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity ( prePostEnabled = true )
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired
private AuthenticationFailureHandler authenticationFailureHandler;
@Autowired
private RedisSessionSecurityContextRepository redisSecurityContextRepository;
@Autowired
private AuthenticationEntryPoint entryPoint;
@Autowired
private CorsConfiguration corsConfiguration;
@Autowired
private SysLogoutHandler sysLogoutHandler;
@Autowired
private SysLogoutSuccessHandler sysLogoutSuccessHandler;
@Autowired
private RedisSessionRegistry redisSessionRegistry;
@Autowired
private TokenSessionAuthenticationStrategy tokenSessionAuthenticationStrategy;
@Bean
public PasswordEncoder passwordEncoder ( ) {
return new BCryptPasswordEncoder ( ) ;
}
public static void main ( String [ ] args) {
System . out. println ( new BCryptPasswordEncoder ( ) . matches ( "123456" , "$2a$10$bwHJ2dlbsgf57X0NsJFtAucqs/cUeaSEc3Nox0VLl4Eo0O3KQMH9C" ) ) ;
}
@Override
public void configure ( WebSecurity web) throws Exception {
web. ignoring ( )
. antMatchers ( "/**/*.css" )
. antMatchers ( "/**/*.js" )
. antMatchers ( "/doc.html" )
. antMatchers ( "/webjars/**" )
. antMatchers ( "/**/*swagger*/**" )
. antMatchers ( "/v2/api-docs" )
. antMatchers ( "/favicon.ico" )
. antMatchers ( "/test/**" )
. antMatchers ( "/static/**" ) ;
}
@Override
protected void configure ( HttpSecurity http) throws Exception {
http
. csrf ( )
. disable ( )
. cors ( )
. configurationSource ( request -> corsConfiguration)
. and ( )
. securityContext ( )
. securityContextRepository ( redisSecurityContextRepository)
. and ( )
. formLogin ( )
. usernameParameter ( "username" )
. passwordParameter ( "password" )
. successHandler ( authenticationSuccessHandler)
. failureHandler ( authenticationFailureHandler)
. and ( )
. logout ( )
. permitAll ( true )
. logoutUrl ( "/logout" )
. addLogoutHandler ( sysLogoutHandler)
. logoutSuccessHandler ( sysLogoutSuccessHandler)
. and ( )
. authorizeRequests ( )
. antMatchers ( "/login/**" ) . permitAll ( )
. anyRequest ( ) . authenticated ( )
. and ( )
. exceptionHandling ( )
. authenticationEntryPoint ( entryPoint)
. and ( )
. sessionManagement ( )
. withObjectPostProcessor ( new ObjectPostProcessor < CompositeSessionAuthenticationStrategy > ( ) {
@Override
public < O extends CompositeSessionAuthenticationStrategy > O postProcess ( O object) {
List < SessionAuthenticationStrategy > list = Arrays . asList ( tokenSessionAuthenticationStrategy) ;
CompositeSessionAuthenticationStrategy strategy = new CompositeSessionAuthenticationStrategy ( list) ;
return ( O ) strategy;
}
} )
;
RedisConcurrentSessionFilter redisConcurrentSessionFilter = ApplicationContextUtil
. getAppContext ( ) . getAutowireCapableBeanFactory ( ) . createBean ( RedisConcurrentSessionFilter . class ) ;
http. addFilterBefore ( redisConcurrentSessionFilter, SessionManagementFilter . class ) ;
}
}
SysUserDetailsService
package com. zzhua. blog. config. security ;
import com. baomidou. mybatisplus. core. conditions. query. QueryWrapper ;
import com. zzhua. blog. config. ex. BizException ;
import com. zzhua. blog. config. redis. RedisConstants ;
import com. zzhua. blog. config. redis. RedisService ;
import com. zzhua. blog. entity. UserAuthEntity ;
import com. zzhua. blog. entity. UserInfoEntity ;
import com. zzhua. blog. enums. CaptchaEnum ;
import com. zzhua. blog. mapper. UserAuthMapper ;
import com. zzhua. blog. mapper. UserInfoMapper ;
import com. zzhua. blog. util. BeanUtil ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. security. core. userdetails. UserDetails ;
import org. springframework. security. core. userdetails. UserDetailsService ;
import org. springframework. security. core. userdetails. UsernameNotFoundException ;
import org. springframework. stereotype. Component ;
import org. springframework. util. StringUtils ;
import javax. servlet. http. HttpServletRequest ;
import java. util. Date ;
import java. util. List ;
import java. util. Objects ;
import java. util. stream. Collectors ;
@Component
public class SysUserDetailsService implements UserDetailsService {
@Autowired
private UserAuthMapper userAuthMapper;
@Autowired
private UserInfoMapper userInfoMapper;
@Autowired
private RedisService redisService;
@Autowired
private HttpServletRequest request;
@Override
public UserDetails loadUserByUsername ( String username) throws UsernameNotFoundException {
UserAuthEntity userAuthEntity = userAuthMapper. selectOne ( new QueryWrapper < UserAuthEntity > ( )
. lambda ( )
. eq ( UserAuthEntity :: getUsername , username)
) ;
if ( userAuthEntity == null ) {
throw BizException . USERNAME_OR_PWD_ERR;
}
UserInfoEntity userInfoEntity = userInfoMapper. selectOne ( new QueryWrapper < UserInfoEntity > ( )
. lambda ( )
. eq ( UserInfoEntity :: getId , userAuthEntity. getUserInfoId ( ) )
) ;
UserDetailDTO userDetailDTO = new UserDetailDTO ( ) ;
BeanUtil . copyBeanProps ( userAuthEntity, userDetailDTO) ;
BeanUtil . copyBeanProps ( userInfoEntity, userDetailDTO) ;
userDetailDTO. setUserInfoId ( userInfoEntity. getId ( ) ) ;
userDetailDTO. setUserAuthId ( userAuthEntity. getId ( ) ) ;
userDetailDTO. setLoginType ( LoginTypeEnum . EMAIL_LOGIN. loginType ( ) ) ;
userDetailDTO. setPassword ( userAuthEntity. getPassword ( ) ) ;
userDetailDTO. setLoginTime ( new Date ( ) ) ;
List < String > permList = userInfoMapper. listPermsForUser ( userInfoEntity. getId ( ) ) ;
userDetailDTO. setPerms ( permList. stream ( ) . filter ( e -> ! StringUtils . isEmpty ( e) ) . collect ( Collectors . toList ( ) ) ) ;
return userDetailDTO;
}
}
2. CorsConfig
package com. zzhua. blog. config. security ;
import com. zzhua. blog. config. redis. RedisConstants ;
import org. springframework. context. annotation. Bean ;
import org. springframework. context. annotation. Configuration ;
import org. springframework. web. cors. CorsConfiguration ;
import java. util. Arrays ;
@Configuration
public class CorsConfig {
@Bean
public CorsConfiguration corsConfiguration ( ) {
CorsConfiguration corsConfiguration = new CorsConfiguration ( ) ;
corsConfiguration. setMaxAge ( 3600L ) ;
corsConfiguration. setAllowCredentials ( true ) ;
corsConfiguration. setAllowedOrigins ( Arrays . asList ( "*" ) ) ;
corsConfiguration. setAllowedMethods ( Arrays . asList ( "*" ) ) ;
corsConfiguration. setAllowedHeaders ( Arrays . asList ( "*" ) ) ;
corsConfiguration. setExposedHeaders ( Arrays . asList ( RedisConstants . AUTHORIZATIO_HEADER) ) ;
return corsConfiguration;
}
}