聊聊SwitchUserFilter的使用
序
本文就来介绍一下如何使用SwitchUserFilter进行账户切换
filter顺序
spring security内置的各种filter:
Alias Filter Class Namespace Element or Attribute CHANNEL_FILTER ChannelProcessingFilter http/intercept-url@requires-channel SECURITY_CONTEXT_FILTER SecurityContextPersistenceFilter http CONCURRENT_SESSION_FILTER ConcurrentSessionFilter session-management/concurrency-control HEADERS_FILTER HeaderWriterFilter http/headers CSRF_FILTER CsrfFilter http/csrf LOGOUT_FILTER LogoutFilter http/logout X509_FILTER X509AuthenticationFilter http/x509 PRE_AUTH_FILTER AbstractPreAuthenticatedProcessingFilter Subclasses N/A CAS_FILTER CasAuthenticationFilter N/A FORM_LOGIN_FILTER UsernamePasswordAuthenticationFilter http/form-login BASIC_AUTH_FILTER BasicAuthenticationFilter http/http-basic SERVLET_API_SUPPORT_FILTER SecurityContextHolderAwareRequestFilter http/@servlet-api-provision JAAS_API_SUPPORT_FILTER JaasApiIntegrationFilter http/@jaas-api-provision REMEMBER_ME_FILTER RememberMeAuthenticationFilter http/remember-me ANONYMOUS_FILTER AnonymousAuthenticationFilter http/anonymous SESSION_MANAGEMENT_FILTER SessionManagementFilter session-management EXCEPTION_TRANSLATION_FILTER ExceptionTranslationFilter http FILTER_SECURITY_INTERCEPTOR FilterSecurityInterceptor http SWITCH_USER_FILTER SwitchUserFilter N/A可以看到SwitchUserFilter是spring security提供的filter里头order顺序在最后的一个。前面讲到了FilterSecurityInterceptor主要用来进行鉴权处理,而SwitchUserFilter是用来做账户切换的,把它放在FilterSecurityInterceptor之后,是要求对切换用户的功能进行鉴权,否则任何人都可以随意切换用户,那就安全故障了。
config
@EnableWebSecurity
@EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public SwitchUserFilter switchUserFilter(UserDetailsService userDetailsService) throws Exception {
SwitchUserFilter switchUserFilter = new SwitchUserFilter();
switchUserFilter.setUserDetailsService(userDetailsService);
switchUserFilter.setTargetUrl("/session");
return switchUserFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//Each <http> namespace block always creates an SecurityContextPersistenceFilter, an ExceptionTranslationFilter and a FilterSecurityInterceptor. These are fixed and cannot be replaced with alternatives.
http
.addFilterAfter(switchUserFilter(userDetailsService()),FilterSecurityInterceptor.class)
.exceptionHandling().authenticationEntryPoint(new UnauthorizedEntryPoint())
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/login","/css/**", "/js/**","/fonts/**").permitAll()
.antMatchers("/session").authenticated()
.antMatchers("/login/impersonate").hasAuthority("ROLE_ADMIN")
.antMatchers("/logout/impersonate").hasAuthority(SwitchUserFilter.ROLE_PREVIOUS_ADMINISTRATOR)
.and()
.formLogin()
.permitAll()
.and()
.logout()
.deleteCookies("JSESSIONID")
.permitAll();
}
@Bean
@Override
protected UserDetailsService userDetailsService(){
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("demoUser1").password("123456")
.authorities("ROLE_USER","read_x").build());
manager.createUser(User.withUsername("admin").password("123456")
.authorities("ROLE_ADMIN").build());
return manager;
}
}
SwitchUserFilter默认的切换账号的url为/login/impersonate,默认注销切换账号的url为/logout/impersonate,默认的账号参数为username
使用
上面的配置为了方便验证,把切换完用户的targetUrl设置为/session,其代码如下
@RestController
@RequestMapping("/session")
public class SessionController {
@GetMapping("")
public Object getCurrentUser(){
return SecurityContextHolder.getContext().getAuthentication();
}
}
首先用普通用户登录,访问http://localhost:8080/login/impersonate?username=admin,发现返回403
注销,使用管理员登录,访问http://localhost:8080/login/impersonate?username=demoUser1,发现成功并跳转到session
{
"authorities": [
{
"authority": "ROLE_USER"
},
{
"authority": "read_x"
},
{
"source": {
"authorities": [
{
"authority": "ROLE_ADMIN"
}
],
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": null
},
"authenticated": true,
"principal": {
"password": null,
"username": "admin",
"authorities": [
{
"authority": "ROLE_ADMIN"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
},
"credentials": null,
"name": "admin"
},
"authority": "ROLE_PREVIOUS_ADMINISTRATOR"
}
],
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": "1BF3D6F40A6F488EFD3ABE8F80E52872"
},
"authenticated": true,
"principal": {
"password": "123456",
"username": "demoUser1",
"authorities": [
{
"authority": "ROLE_USER"
},
{
"authority": "read_x"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
},
"credentials": "123456",
"name": "demoUser1"
}
可以发现有成功切换
之后再切换回来
http://localhost:8080/logout/impersonate?username=demoUser1
{
"authorities": [
{
"authority": "ROLE_ADMIN"
}
],
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": null
},
"authenticated": true,
"principal": {
"password": null,
"username": "admin",
"authorities": [
{
"authority": "ROLE_ADMIN"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
},
"credentials": null,
"name": "admin"
}
可以发现切换回来了,是不是非常神奇,太强大了,以后线上排查问题之类的,非常方便,爽歪歪了简直异常情况
如果你切换了不存在的用户,则报
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sat Dec 16 14:36:28 CST 2017
There was an unexpected error (type=Unauthorized, status=401).
Authentication Failed: demoUser2
小结
SwitchUserFilter是个强大的filter,非常方便测试环境进行调试、测试,甚至可以用来进行上线问题排查。
文章来源:
Author:xixicat
link:https://segmentfault.com/a/1190000012483631
上一篇: 聊聊二维码登录