Spring Security 自定义 登陆 权限验证

项目简介

原创 libertinus 出处Spring Security 自定义 登陆 权限验证

基于Spring Cloud 的项目,Spring Cloud是在Spring Boot上搭建的所以按照Spring Boot的方式来写

Spring Security 配置

继承 WebSecurityConfigurerAdapter ,重写configure(HttpSecurity http)配置相关权限以及重写拦截器

     http.authorizeRequests()
        .antMatchers("/auth/**").permitAll()
        .anyRequest().authenticated().and()
        //证书 认证 自动登陆
        .addFilterBefore(authTokenFilter, UsernamePasswordAuthenticationFilter.class)
        //登陆以及权限控制Filter
        ......
    ;

自定义UsernamePasswordAuthenticationFilter

自定义 UsernamePasswordAuthenticationFilter 实现自动登陆
创建Authentication 模拟登陆

Authentication authentication = new UsernamePasswordAuthenticationToken(auth, token);
SecurityContextHolder.getContext().setAuthentication(authentication);;

自定义FilterSecurityInterceptor

Spring Security 是通过这个过滤器来实现 Http资源安全过滤的。

获取资源权限

FilterSecurityInterceptor继承自 AbstractSecurityInterceptor ,源码中的其中beforeInvocation方法的一段代码是:

Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
                .getAttributes(object);

这个方法是来获取资源权限 ,可以重写SecurityMetadataSource obtainSecurityMetadataSource(){}方法来实现,传入一个FilterInvocation对象,返回一个Collection<ConfigAttribute>对象。
这个对象中可以获取到request, response等内置对象,可以通过一下代码来匹配

RequestMatcher requestMatcher = new AntPathRequestMatcher("/manager/**");
if(requestMatcher.matches(request)){
    return RESOURCE            
}

ConfigAttribute 可以通过new SecurityConfig((String)input) 来创建

编写认证提供者

重写 AuthenticationManager 实现,用户登陆可以放这里面

Authentication authenticate(Authentication authentication)
            throws AuthenticationException;

用来生成Authentication, 原始的够用的话直接注入设置就好。

用户是否有获取资源权限

AbstructSecurityIntercepter 中的一下方法来判断用户权限是否可以拥有该资源

this.accessDecisionManager.decide(authenticated, object, attributes);

为了达到自定义控制的目的,我们需要实现AccessDecisionManager接口,来重写这个方法,如果判断不通过 decide方法可以抛出AccessDeniedException,来阻止用户访问

/**
 * 判断用户是否有访问资源权限
 * @param authentication    用户Auth
 * @param object FilterInvocation对象
 * @param configAttributes  资源所需权限
 * @throws AccessDeniedException  无权限Exception
 * @throws InsufficientAuthenticationException
 */
public void decide(Authentication authentication, Object object,
                       Collection<ConfigAttribute> configAttributes)
            throws AccessDeniedException, InsufficientAuthenticationException {
                if(access){
                    //允许通过
                    return;
                }
                //不允许角色访问
                throw new AccessDeniedException("NO ALLOW");
            }

JAVA 源码片

WebSecurityConfig

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private AuthTokenFilter authTokenFilter;   
    @Autowired
    private ApiPermissionSecurityFilter securityFilter;
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/auth/**").permitAll()
            .anyRequest().authenticated().and()
            //证书 认证 自动登陆
            .addFilterBefore(authTokenFilter, UsernamePasswordAuthenticationFilter.class)
            //登陆以及权限控制Filter
            .addFilterBefore(securityFilter, FilterSecurityInterceptor.class)
            .csrf().disable()
            //基于Token 不需要Session
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        ;
    }
}

AuthTokenFilter (自定义UsernamePasswordAuthenticationFilter)

@Component
public class AuthTokenFilter extends OncePerRequestFilter {
    @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            String auth = request.getHeader("Authorization");
            //用户登陆,暂不设置权限
            Token token = new Token(auth, null);
            Authentication authentication = new UsernamePasswordAuthenticationToken(auth, token);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            filterChain.doFilter(request, response);
        }
}

ApiPermissionSecurityFilter

@Component
public class ApiPermissionSecurityFilter extends AbstractSecurityInterceptor implements Filter {
    @Autowired
    private ApiInvocationSecurityMetadataSourceService apiInvocationSecurityMetadataSourceService;
    @Autowired
    private ApiAccessDecisionManager apiAccessDecisionManager;
    @Autowired
    private AuthenticationManager authenticationManager;

    @PostConstruct
    public void init(){
        super.setAuthenticationManager(authenticationManager);
        super.setAccessDecisionManager(apiAccessDecisionManager);
    }
    
    public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException{
        FilterInvocation fi = new FilterInvocation( request, response, chain );
        invoke(fi);
    }

    public Class<? extends Object> getSecureObjectClass(){
        return FilterInvocation.class;
    }

    public void invoke( FilterInvocation fi ) throws IOException, ServletException{
        InterceptorStatusToken  token = super.beforeInvocation(fi);
        try{
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        }finally{
            super.afterInvocation(token, null);
        }
    }


    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource(){
        return this.apiInvocationSecurityMetadataSourceService;
    }
    public void destroy(){
    }
    public void init( FilterConfig filterconfig ) throws ServletException{
    }
}

ApiInvocationSecurityMetadataSourceService

/**
 * 资源-权限控制对象
 * Created by liang on 2017/3/17.
 */
@Component
public class ApiInvocationSecurityMetadataSourceService implements
        FilterInvocationSecurityMetadataSource {
    //缓存 英文名-权限
    private static LoadingCache<String, Collection<ConfigAttribute>> permitMap = null;
    //缓存 英文名-ODCINFO信息对象
    private static LoadingCache<String, OdcInfo> odcInfoMap = null;
    @PostConstruct
    private void init() {
        //资源启动时初始化 资源和角色权限
        //缓存 英文名-权限 初始化
        //缓存 英文名-ODCINFO
    }
    
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        FilterInvocation filterInvocation = (FilterInvocation) object;
        //TODO 干你想干事情,下面是获取路径所具有的资源
        return permitMap.get(getHttpRequest().getRequestURI());
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return new ArrayList<ConfigAttribute>();
    }

    @Override
    public boolean supports(Class<?> aClass) {
        //很重要,不然不起作用
        return true;
    }
}

ApiAccessDecisionManager

@Component
public class ApiAccessDecisionManager implements AccessDecisionManager {
    /**
     * 判断用户是否有访问资源权限
     * @param authentication    用户Auth
     * @param object FilterInvocation对象
     * @param configAttributes  资源所需权限
     * @throws AccessDeniedException  无权限Exception
     */
    public void decide(Authentication authentication, Object object,
               Collection<ConfigAttribute> configAttributes)
               throws AccessDeniedException {
        if(access){
            //允许通过
            return;
        }
        //不允许角色访问
        throw new AccessDeniedException("NO ALLOW");
    }
    
    public boolean supports( ConfigAttribute attribute ){
            return true;
    }

    public boolean supports(Class<?> clazz){
        return true;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,968评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,682评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,254评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,074评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,964评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,055评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,484评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,170评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,433评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,512评论 2 308
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,296评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,184评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,545评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,150评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,437评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,630评论 2 335

推荐阅读更多精彩内容