spring security免登陆动态配置方案2

以前有篇文章讲了怎么进行免登陆动态配置的方案,动用了反射去实现,有点黑魔法的味道,这里再介绍另一种方案css

permitAll

spring-security-config-4.2.3.RELEASE-sources.jar!/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.javajava

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添加到REGISTRYweb

思路

@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。spring

因而咱们的思路就来了,新建一个filter,插入在AnonymousAuthenticationFilter以前,对于免登陆的设置为authenticatedsegmentfault

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;
    }
}

这里建立了一个伪造的UsernamePasswordAuthenticationTokensession

这里有一点要注意一下,就在判断不是配置的容许匿名访问的url的时候,若是以前的token是咱们设置的,则须要从新清空,防止一旦访问匿名url以后获取session再去越权访问其余没有配置的url。app

配置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。ide

小结

这样基本就大功告成了,不过有几点须要注意:ui

  • 自定义的filter,可能存在执行两遍的问题,这点后面的文章来说
  • 获取到的uri没法处理pathvariable的状况,须要根据url pattern来处理,这点后面再讲述一下

doc

相关文章
相关标签/搜索