序
之前有篇文章讲了怎么进行免登录动态配置的方案,动用了反射去实现,有点黑魔法的味道,这里再介绍另外一种方案
permitAll
spring-security-config-4.2.3.RELEASE-sources.jar!/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java
public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
extends
AbstractInterceptUrlConfigurer<ExpressionUrlAuthorizationConfigurer<H>, H> {
static final String permitAll = "permitAll";
private static final String denyAll = "denyAll";
private static final String anonymous = "anonymous";
private static final String authenticated = "authenticated";
private static final String fullyAuthenticated = "fullyAuthenticated";
private static final String rememberMe = "rememberMe";
private final ExpressionInterceptUrlRegistry REGISTRY;
//......
/**
* Specify that URLs are allowed by anyone.
*
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
public ExpressionInterceptUrlRegistry permitAll() {
return access(permitAll);
}
public ExpressionInterceptUrlRegistry access(String attribute) {
if (not) {
attribute = "!" + attribute;
}
interceptUrl(requestMatchers, SecurityConfig.createList(attribute));
return ExpressionUrlAuthorizationConfigurer.this.REGISTRY;
}
private void interceptUrl(Iterable<? extends RequestMatcher> requestMatchers,
Collection<ConfigAttribute> configAttributes) {
for (RequestMatcher requestMatcher : requestMatchers) {
REGISTRY.addMapping(new AbstractConfigAttributeRequestMatcherRegistry.UrlMapping(
requestMatcher, configAttributes));
}
}
}
permitAll操作将“permitAll”这个attribute以及对应的requestMatchers添加到REGISTRY
思路
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**", "/js/**","/fonts/**").permitAll()
.anyRequest().authenticated();
}
}
这里重点注意这个anyRequest().authenticated(),可以看到没有配置permitAll的请求,都要求authenticated这个级别的,而AnonymousAuthenticationFilter设置的匿名级别只是anonymous。
于是我们的思路就来了,新建一个filter,插入在AnonymousAuthenticationFilter之前,对于免登录的设置为authenticated
DemoFilter
public class DemoFilter extends GenericFilterBean {
private Object principal = "annoUser";
private List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_ANNO");
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
private final Map<String,HttpMethod[]> annoPatternMap = new HashMap<String,HttpMethod[]>(){{
//for demo, you can change it and read from db or else
put("/index/demo",new HttpMethod[]{HttpMethod.GET});
}};
String uri = ((HttpServletRequest) servletRequest).getRequestURI();
if(annoPatternMap.containsKey(uri)){
if(SecurityContextHolder.getContext().getAuthentication() == null){
SecurityContextHolder.getContext().setAuthentication(
createAuthentication((HttpServletRequest) servletRequest));
}
}else{
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println(auth == null);
if(auth != null && auth instanceof UsernamePasswordAuthenticationToken){
if(principal.toString().equals(auth.getPrincipal().toString())){
SecurityContextHolder.getContext().setAuthentication(null);
}
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
protected Authentication createAuthentication(HttpServletRequest request) {
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
principal, "N/A", authorities);
auth.setDetails(authenticationDetailsSource.buildDetails(request));
return auth;
}
}
这里创建了一个伪造的UsernamePasswordAuthenticationToken
这里有一点要注意一下,就在判断不是配置的允许匿名访问的url的时候,如果之前的token是我们设置的,则需要重新清空,防止一旦访问匿名url之后获取session再去越权访问其他没有配置的url。
配置filter
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(new DemoFilter(),AnonymousAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/css/**", "/js/**","/fonts/**").permitAll()
.anyRequest().authenticated();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin").password("admin").roles("USER");
}
}
在AnonymousAuthenticationFilter之前提前设置好SecurityContextHolder里头的authentication。
小结
这样基本就大功告成了,不过有几点需要注意:
- 自定义的filter,可能存在执行两遍的问题,这点后面的文章来讲
- 获取到的uri无法处理pathvariable的情况,需要根据url pattern来处理,这点后面再讲述一下