• 权限控制-spring security 第三方登录与数据库配置方案详解


    详情参考:https://springcloud.cc/spring-security-zhcn.html

    Spring security 提供了接口,依据这些接口可以自定义自己的校验规则。

    AccessDecisionManager 权限校验

    FilterInvocationSecurityMetadataSource 权限配置数据库加载

    AbstractSecurityInterceptor  Spring security 核心抽象接口

    AuthenticationManager  自定义用户角色数据

    WebSecurityConfigurerAdapter Spring security核心配置

     

    目前需求场景:cas第三方登录,返回cookies,然后微服务后台负责将用户信息放到缓存中去。不使用spring security的登陆。通过拦截直接将用户信息放到SecurityContextHolder.getContext() 中。然后通过 AbstractSecurityInterceptor 去组合数据库加载配置FilterInvocationSecurityMetadataSource 和自定义校验 AccessDecisionManager

    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        private MyFilterSecurityInterceptor myFilterSecurityInterceptor;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
              //配置白名单 .antMatchers(
    "/swagger*","/webjars/**","/v2/**","/metrics").permitAll() .antMatchers("/api/claims/**").permitAll()
              //这些接口访问需要登录 .antMatchers(
    "/api/**").authenticated() // .access("hasRole('ROLE_R_ICORE_AIMS_NEWS_CONFIG')") .and() .csrf().disable();
        //拦截和校验请求 http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.
    class); } }

    拦截和校验,由于是微服务,当用户请求到某个微服务,需要确保当前微服务拥有该用户信息,然后在使用自定义增加配置和校验

    1)查询一下当前用户,如果当前用户是默认用户,则添加用户信息到SecurityContextHolder.getContext()中

    2)使用自定义校验

    @Service
    public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
        private Logger log = LoggerFactory.getLogger(MyFilterSecurityInterceptor.class);
    
        @Autowired
        private FilterInvocationSecurityMetadataSource securityMetadataSource;
    //针对某些接口放白名单
        private String[] ignoreStartUris = new String[]{
                "/swagger",
                "/webjars/",
                "/v2/",
                "/api/claims/",
                "/metrics"
        };
    //设置自定义校验
        @Autowired
        public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
            super.setAccessDecisionManager(myAccessDecisionManager);
        }
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
            FilterInvocation fi = new FilterInvocation(request, response, chain);
            invoke(fi);
        }
    
    
        public void invoke(FilterInvocation fi) throws IOException, ServletException {
            //通过cookie获取用户信息
            HttpServletRequest request = fi.getHttpRequest();
            String url = request.getServletPath();
            if(StringUtils.startsWithAny(url, ignoreStartUris)){
                //执行下一个拦截器
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
                return;
            }
    //        SecurityContextHolder.clearContext();
            String principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString();
            log.info("[权限拦截] 当前登录用户 user:{}",principal);
            log.info("[权限拦截] 当前用户角色:{}", JSON.toJSON( SecurityContextHolder.getContext().getAuthentication().getAuthorities()));
            if("anonymousUser".equals(principal)){
                log.info("[权限拦截] 给用户授权开始========url:{}",url);
                Cookie[] cookies = request.getCookies();
                String sessionId = null;
                if(cookies!=null){
                    for (Cookie cookie : cookies) {
                        if (cookie.getName().equals(Constants.CAS_SESSION_ID)) {
                            sessionId = cookie.getValue();
                            break;
                        }
                    }
                }
                SessionUser sessionUser= UserUtils.getUserBySessionId(sessionId);
                if(sessionUser!=null){
                    Authentication request1 = new UsernamePasswordAuthenticationToken(sessionUser.getUid(), sessionUser.getUid());
                    SampleAuthenticationManager am = new SampleAuthenticationManager();
                    am.setRoles(sessionUser.getRoles());
                    Authentication result = am.authenticate(request1);
                    SecurityContextHolder.getContext().setAuthentication(result);
                }else {
                    log.info("[权限校验] 当前用户未登录");
                }
                log.info("[权限拦截] 当前用户角色:{}", JSON.toJSON( SecurityContextHolder.getContext().getAuthentication().getAuthorities()));
                log.info("[权限拦截] 给用户授权结束========");
            }
        //fi里面有一个被拦截的url
        //里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
        //再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
            InterceptorStatusToken token = super.beforeInvocation(fi);
            if (token==null){
                return;
            }
            try {
        //执行下一个拦截器
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            } finally {
                super.afterInvocation(token, null);
            }
    
        }
    
        @Override
        public void destroy() {
    
        }
    
    //设置自定义数据库配置
        @Override
        public SecurityMetadataSource obtainSecurityMetadataSource() {
            return this.securityMetadataSource;
        }
    
        @Override
        public Class<?> getSecureObjectClass() {
            return FilterInvocation.class;
        }
    }

     AccessDecisionManager 权限校验

    @Service
    public class MyAccessDecisionManager implements AccessDecisionManager {
    
        // decide 方法是判定是否拥有权限的决策方法,
        //authentication 是释CustomUserService中循环添加到 GrantedAuthority 对象中的权限信息集合.
        //object 包含客户端发起的请求的requset信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
        //configAttributes 为MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
        @Override
        public void decide(Authentication authentication,
                           Object object,
                           Collection<ConfigAttribute> configAttributes)
                throws AccessDeniedException, InsufficientAuthenticationException {
    
            if(null== configAttributes || configAttributes.size() <=0) {
                return;
            }
            ConfigAttribute c;
            String needRole;
            for(Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) {
                c = iter.next();
                needRole = c.getAttribute();
                for(GrantedAuthority ga : authentication.getAuthorities()) {
                    //authentication 为在注释1 中循环添加到 GrantedAuthority 对象中的权限信息集合
                    if(needRole.trim().equals(ga.getAuthority())) {
                        return;
                    }
                }
            }
            throw new AccessDeniedException("no right");
        }
    
        @Override
        public boolean supports(ConfigAttribute attribute) {
            return true;
        }
    
        @Override
        public boolean supports(Class<?> clazz) {
            return true;
        }
    }

    FilterInvocationSecurityMetadataSource 权限配置数据库加载

    @Service
    public class MyInvocationSecurityMetadataSourceService implements
            FilterInvocationSecurityMetadataSource {
    
        @Autowired
        private PermissionDao permissionDao;
    
        private HashMap<String, Collection<ConfigAttribute>> map =null;
    
        /**
         * 加载权限表中所有权限
         */
        public void loadResourceDefine(){
            map = new HashMap<>();
            ConfigAttribute cfg;
            List<AimsPermission> permissions = permissionDao.findAll();
            for(AimsPermission permission : permissions) {
                cfg = new SecurityConfig(permission.getName());
                //此处只添加了用户的名字,其实还可以添加更多权限的信息,
                // 例如请求方法到ConfigAttribute的集合中去。此处添加的信息将会作为MyAccessDecisionManager类的decide的第三个参数。
                //用权限的getUrl() 作为map的key,用ConfigAttribute的集合作为 value,
                setMap(permission.getUrl(),cfg);
            }
    
        }
    
        /**
         * 增加权限队列
         * @param url
         * @param cfg
         */
        private void  setMap(String url,ConfigAttribute cfg){
            Collection<ConfigAttribute> array=map.get(url);
            if(CollectionUtils.isEmpty(array)){
                array = new ArrayList<>(6);
            }
            array.add(cfg);
            map.put(url, array);
        }
    
        /**
         *  此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,
         // 则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
         */
        @Override
        public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
            if(map ==null) {
    
                loadResourceDefine();
            }
            //object 中包含用户请求的request 信息
            HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
            AntPathRequestMatcher matcher;
            String resUrl;
            for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) {
                resUrl = iter.next();
                matcher = new AntPathRequestMatcher(resUrl);
                if(matcher.matches(request)) {
                    return map.get(resUrl);
                }
            }
            return null;
        }
    
        @Override
        public Collection<ConfigAttribute> getAllConfigAttributes() {
            return null;
        }
    
        @Override
        public boolean supports(Class<?> clazz) {
            return true;
        }
    }

    AuthenticationManager  自定义用户角色数据

    public class SampleAuthenticationManager implements AuthenticationManager {
        List<GrantedAuthority> AUTHORITIES = new ArrayList<GrantedAuthority>();
    
        private String prd="ROLE_";
        public List<String> getRoles() {
            return roles;
        }
    
        public void setRoles(List<String> roles) {
            this.roles = roles;
        }
    
        private List<String> roles;
    
    
        @Override
        public Authentication authenticate(Authentication auth) throws AuthenticationException {
            if (auth.getName().equals(auth.getCredentials())) {
                for (String role:roles) {
            //增加用户角色 AUTHORITIES.add(
    new SimpleGrantedAuthority(prd+role)); } return new UsernamePasswordAuthenticationToken(auth.getName(), auth.getCredentials(), AUTHORITIES); } throw new BadCredentialsException("Bad Credentials"); } }

    模拟数据库获取数据,可以具体到每个url对应的可以访问角色。

    @Component
    public class PermissionDao {
    
        public List<AimsPermission> findAll(){
            List<AimsPermission> list=new ArrayList<>(2);
            AimsPermission aimsPermission1=new AimsPermission();
            aimsPermission1.setName("ROLE_R_ICORE_AIMS_NEWS_CONFIG");
            aimsPermission1.setUrl("/api/helper/queryByNameAndIdentityNo");
            list.add(aimsPermission1);
            AimsPermission aimsPermission=new AimsPermission();
            aimsPermission.setName("ROLE_R_ICORE_AIMS_CHECK");
            aimsPermission.setUrl("/api/helper/queryByNameAndIdentityNo");
            AimsPermission aimsPermission2=new AimsPermission();
            aimsPermission2.setName("ROLE_R_ICORE_AIMS_CHECK");
            aimsPermission2.setUrl("/api/aimsinUser/getCurrentUserInfo");
            list.add(aimsPermission2);
    
            return list;
        }
    }
    学习的时间不一定要特定安排
  • 相关阅读:
    vue token使用 参考
    token 的作用与使用
    jq 绑定实时监听 input输入框
    认识java
    java基础语法
    java虚拟机笔记 运行时内存区域划分
    spring全家桶
    利用python脚本统计和删除redis key
    MySQL中count(字段) ,count(主键 id) ,count(1)和count(*)的区别
    编写shell脚本的一些规范
  • 原文地址:https://www.cnblogs.com/zhongzheng123/p/9199205.html
Copyright © 2020-2023  润新知