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

相關文章
相關標籤/搜索