权限控制-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;
    }
}

 

posted @ 2018-06-20 16:08  钟政123  阅读(1638)  评论(0编辑  收藏  举报