前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >spring security免登录动态配置方案2

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

作者头像
code4it
发布2018-09-17 15:23:19
1.4K0
发布2018-09-17 15:23:19
举报
文章被收录于专栏:码匠的流水账码匠的流水账

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

permitAll

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

代码语言:javascript
复制
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

思路

代码语言:javascript
复制
@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

代码语言:javascript
复制
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

代码语言:javascript
复制
@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来处理,这点后面再讲述一下

doc

  • spring security运行时配置ignore url
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-11-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码匠的流水账 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • permitAll
  • 思路
  • DemoFilter
  • 配置filter
  • 小结
  • doc
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档