美文网首页
Spring Security 自定义 登陆 权限验证

Spring Security 自定义 登陆 权限验证

作者: libertinus | 来源:发表于2017-04-21 15:42 被阅读0次

    项目简介

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

    相关文章

      网友评论

          本文标题:Spring Security 自定义 登陆 权限验证

          本文链接:https://www.haomeiwen.com/subject/mivdzttx.html