资讯专栏INFORMATION COLUMN

Spring Secutity 自定义权限配置

animabear / 2020人阅读

摘要:实现了接口,用于存放用户信息与权限在身份认证中的作用中进行身份验证的是接口,是它的一个默认实现,但它并不用来处理身份认证,而是委托给配置好的,每个会轮流检查身份认证。检查后或者返回对象或者抛出异常。另外自定义接口,实现类为。

Srping Security网上也有很多例子,但基本都是所资源直接配置在XML文件里,限制太大,不够灵活。我们需要的是可以在后台修改资源访问权限,实时生效,才能符合现在大多数系统的需求。

需要引入的依赖
    
    
        org.springframework.security
        spring-security-web
        4.2.2.RELEASE
    
    
        org.springframework.security
        spring-security-config
        4.2.2.RELEASE
    
    
用户身份认证

我们自定义一个实现类MUserDetailsService 来实现UserDetailsService接口。

其中需要实现一个loadUserByUsername方法,用来读取用户的角色。
在这里需要从数据库中通过用户名来查询用户的信息和用户所属的角色
其中MGrantedAuthority实现了GrantedAuthority接口,用于构建用户权限。
MUserDeatils实现了UserDeatils接口,用于存放用户信息与权限

UserDetailsService在身份认证中的作用

Spring Security中进行身份验证的是AuthenticationManager接口,ProviderManager是它的一个默认实现,但它并不用来处理身份认证,而是委托给配置好的AuthenticationProvider,每个AuthenticationProvider会轮流检查身份认证。检查后或者返回Authentication对象或者抛出异常。验证身份就是加载响应的UserDetails,看看是否和用户输入的账号、密码、权限等信息匹配。此步骤由实现AuthenticationProvider的DaoAuthenticationProvider(它利用UserDetailsService验证用户名、密码和授权)处理。包含 GrantedAuthority 的 UserDetails对象在构建 Authentication对象时填入数据。

MUserDetailsService.class中的loadUserByUsername方法
    /**
     * 根据用户名加载用户密码与权限信息
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        //查询用户信息
        User user = userMapper.selectByName(username);
        List roleList = null;
        MUserDeatils userDeatils = null;
        if (user != null){
            //查询用户的角色
            roleList = roleMapper.queryByUser(user.getId());
            System.out.println("user" + user.getUsername() + "----" + user.getPassword());
            // 构建权限
            Set authorities = new HashSet();
            if (roleList.size() != 0){
                for (Role role: roleList){
                    authorities.add(new MGrantedAuthority(role.getName()));
                    System.out.println(role.getName());
                }
                userDeatils = new MUserDeatils(user.getUsername(),user.getPassword(),authorities);

            }
        }
        return userDeatils;

    }
MGrantedAuthority.class
public class MGrantedAuthority implements GrantedAuthority {

    private String authority;

    public MGrantedAuthority(String authority){
        this.authority = authority;
    }

    @Override
    public String getAuthority() {
        return authority;
    }
}
MUserDeatils.class

实现UserDetails接口定义好变量即可

读取资源与所属角色

需要自定义实现类实现FilterInvocationSecurityMetadataSource接口。通过loadResourceDefine方法可以实现资源与权限的对应关系。

要使我们自定义的MFilterInvocationSecurityMetadataSource生效,我们还需要定义一个MyFilterSecurityInterceptor类。
这里的数据需要从数据库中取得。另外自定义接口UrlMatcher,实现类为AntUrlPathMatcher。

网上有教程是把loadResourceDefine方法放在了构造函数里。但我经过多次试验均出现service,mapper无法注入的问题,然后就会报一个空指针的导异常,经debug发现是service没有注入。经多次查询得知:原因是构造方法会先于注入执行,所以loadResourceDefine方法放入构造中执行时函数内的service与mapper还未执行注入,因此报 java.lang.NullPointerException的异常。解决方法是将loadResourceDefine方法放在getAttributes方法中执行。

MFilterInvocationSecurityMetadataSource.class
    @Component
    public class MFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
    @Autowired
    public IRescAndRoleService iRescAndRoleService ;
    @Autowired
    private IUserService iUserService ;
    private UrlMatcher urlMatcher = new AntUrlPathMatcher();
    // 资源权限集合
    private static Map> resourceMap = null;
    public void loadResourceDefine(){
        resourceMap = new HashMap>();
        //取得用户信息
        List userList = iUserService.query();
       //取得资源与角色列表
        List resourceList = iRescAndRoleService.query();
        System.out.println(resourceList);
        for (RescAndRole resource : resourceList) {
            Collection atts = new ArrayList();
            atts.add(new SecurityConfig(resource.getRoleName() ));
            resourceMap.put(resource.getResString(), atts);
        }
    }
    @Override
    public Collection getAttributes(Object o) throws IllegalArgumentException {
        loadResourceDefine();//防止无法注入问题
        // guess object is a URL.
        String url = ((FilterInvocation) o).getRequestUrl();
        Iterator ite = resourceMap.keySet().iterator();
        while (ite.hasNext()) {
            String resURL = ite.next();
            if (urlMatcher.pathMatchesUrl(resURL, url)) {
                return resourceMap.get(resURL);
            }
        }
        return null;
    }
    @Override
    public Collection getAllConfigAttributes() {
        return null;
    }
    @Override
    public boolean supports(Class aClass) {
        return true;
    }
}
AntUrlPathMatcher.class
public class AntUrlPathMatcher implements UrlMatcher {

    private boolean requiresLowerCaseUrl;
    private PathMatcher pathMatcher;

    public AntUrlPathMatcher() {
        this(true);
    }

    public AntUrlPathMatcher(boolean requiresLowerCaseUrl) {
        this.requiresLowerCaseUrl = true;
        this.pathMatcher = new AntPathMatcher();

        this.requiresLowerCaseUrl = requiresLowerCaseUrl;
    }

    public Object compile(String path) {
        if (this.requiresLowerCaseUrl) {
            return path.toLowerCase();
        }
        return path;
    }

    public void setRequiresLowerCaseUrl(boolean requiresLowerCaseUrl) {
        this.requiresLowerCaseUrl = requiresLowerCaseUrl;
    }

    public boolean pathMatchesUrl(Object path, String url) {
        if (("/**".equals(path)) || ("**".equals(path))) {
            return true;
        }
        return this.pathMatcher.match((String) path, url);
    }

    public String getUniversalMatchPattern() {
        return "/**";
    }

    public boolean requiresLowerCaseUrl() {
        return this.requiresLowerCaseUrl;
    }

    public String toString() {
        return super.getClass().getName() + "[requiresLowerCase=""
                + this.requiresLowerCaseUrl + ""]";
    }
}
MyFilterSecurityInterceptor.class
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor
        implements Filter {
    private FilterInvocationSecurityMetadataSource securityMetadataSource;
    
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        invoke(fi);
    }

    public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
        return this.securityMetadataSource;
    }

    public Class 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);
        }
    }

    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return this.securityMetadataSource;
    }

    public void setSecurityMetadataSource(
            FilterInvocationSecurityMetadataSource newSource) {
        this.securityMetadataSource = newSource;
    }

    public void destroy() {
    }

    public void init(FilterConfig arg0) throws ServletException {
    }

}
决策管理器

自定义一个决策管理器MyAccessDecisionManager实现AccessDecisionManager接口。其中的decide方法,决定某一个用户是否有权限访问某个url

/* (non-Javadoc)
     * @see org.springframework.security.access.AccessDecisionManager#decide(org.springframework.security.core.Authentication, java.lang.Object, java.util.Collection)
     * 该方法决定该权限是否有权限访问该资源,其实object就是一个资源的地址,authentication是当前用户的
     * 对应权限,如果没登陆就为游客,登陆了就是该用户对应的权限
     */
    @Override
    public void decide(Authentication authentication, Object object,
                       Collection configAttributes)
            throws AccessDeniedException, InsufficientAuthenticationException {
        if(configAttributes == null) {
            return;
        }
        System.out.println(object.toString()); // object is a URL.
        //所请求的资源拥有的权限(一个资源对多个权限)
        Iterator iterator = configAttributes.iterator();
        while(iterator.hasNext()) {
            ConfigAttribute configAttribute = iterator.next();
            //访问所请求资源所需要的权限
            String needPermission = configAttribute.getAttribute();
            System.out.println("访问"+object.toString()+"需要的权限是:" + needPermission);
            //用户所拥有的权限authentication
            Collection authorities = authentication.getAuthorities();
            for(GrantedAuthority ga : authorities) {
                if(needPermission.equals(ga.getAuthority())) {
                    return;
                }
            }
        }
        //没有权限
        throw new AccessDeniedException(" 没有权限访问! ");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        // TODO Auto-generated method stub
        return true;
    }

    @Override
    public boolean supports(Class clazz) {
        // TODO Auto-generated method stub
        return true;
    }
配置XML web.xml

添加Seucrity的过滤器,将拦截所有资源访问

注意
    只能配置成 /*
    
    
    
        contextConfigLocation
        
            WEB-INF/config/security.xml
            WEB-INF/config/spring-mybatis.xml
        
    
    
    
    
        springSecurityFilterChain
        org.springframework.web.filter.DelegatingFilterProxy
    
    
        springSecurityFilterChain
        /*
    
spring-security.xml



    
    
    
    
    
    
    
    
    
    
    

        
        
        
        
        
        
        
        

    

    
    
        
        
        
        
        
        
        
    

    
    
        
            
        
    

    
    

    
    

    
    

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/67422.html

相关文章

  • 基于 Spring Session & Spring Security 微服务权限控制

    摘要:构造函数的第一个参数是对象,所以可以自定义缓存对象。在微服务各个模块获取用户的这些信息的方法如下略权限控制启用基于方法的权限注解简单权限校验例如,删除角色的接口,仅允许拥有权限的用户访问。 showImg(https://segmentfault.com/img/remote/1460000019593311); 微服务架构 showImg(https://segmentfault.c...

    clasnake 评论0 收藏0
  • Shiro【授权、整合Spirng、Shiro过滤器】

    摘要:表示对用户资源进行操作,相当于,对所有用户资源实例进行操作。与整合,实际上的操作都是通过过滤器来干的。将安全管理器交由工厂来进行管理。在过滤器链中设置静态资源不拦截。 前言 本文主要讲解的知识点有以下: Shiro授权的方式简单介绍 与Spring整合 初始Shiro过滤器 一、Shiro授权 上一篇我们已经讲解了Shiro的认证相关的知识了,现在我们来弄Shiro的授权 Shir...

    ralap 评论0 收藏0
  • Spring Security 进阶-细节总结

    摘要:但是我们最好不要在里面对他进行处理,而是放到配置的权限异常来处理。记得配置登录认证前和过程中的一些请求不需要身份认证。登录认证失败不能直接抛出错误,需要向前端响应异常。 关于 Spring Security 的学习已经告一段落了,刚开始接触该安全框架感觉很迷茫,总觉得没有 Shiro 灵活,到后来的深入学习和探究才发现它非常强大。简单快速集成,基本不用写任何代码,拓展起来也非常灵活和强...

    LinkedME2016 评论0 收藏0
  • 不用 Spring Security 可否?试试这个小而美的安全框架

    摘要:写在前面在一款应用的整个生命周期,我们都会谈及该应用的数据安全问题。用户的合法性与数据的可见性是数据安全中非常重要的一部分。 写在前面 在一款应用的整个生命周期,我们都会谈及该应用的数据安全问题。用户的合法性与数据的可见性是数据安全中非常重要的一部分。但是,一方面,不同的应用对于数据的合法性和可见性要求的维度与粒度都有所区别;另一方面,以当前微服务、多服务的架构方式,如何共享Sessi...

    toddmark 评论0 收藏0
  • 《 Kotlin + Spring Boot : 下一代 Java 服务端开发 》

    摘要:下一代服务端开发下一代服务端开发第部门快速开始第章快速开始环境准备,,快速上手实现一个第章企业级服务开发从到语言的缺点发展历程的缺点为什么是产生的背景解决了哪些问题为什么是的发展历程容器的配置地狱是什么从到下一代企业级服务开发在移动开发领域 《 Kotlin + Spring Boot : 下一代 Java 服务端开发 》 Kotlin + Spring Boot : 下一代 Java...

    springDevBird 评论0 收藏0

发表评论

0条评论

animabear

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<