SpringSecurity实现动态加载权限信息的方法

实现动态加载权限信息的方法是Spring Security中非常重要的一部分,可以根据用户的动态信息进行精确的授权管理。下面是详细的实现攻略。

1. 编写权限信息源的代码

Spring Security中支持自定义的权限信息源,我们需要实现 org.springframework.security.access.vote.RoleVoter 接口并提供动态的用户权限信息。

public class CustomRoleVoter implements RoleVoter {

    private Map<String, List<ConfigAttribute>> roleMap;

    public CustomRoleVoter(Map<String, List<ConfigAttribute>> roleMap) {
        this.roleMap = roleMap;
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return attribute.getAttribute() != null && attribute.getAttribute().startsWith("ROLE_");
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }

    @Override
    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
        int result = ACCESS_ABSTAIN;
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        for (ConfigAttribute attribute : attributes) {
            if (this.supports(attribute)) {
                result = ACCESS_DENIED;
                String roleName = attribute.getAttribute();
                List<ConfigAttribute> configs = roleMap.get(roleName);
                if (configs != null && !configs.isEmpty()) {
                    for (ConfigAttribute configAttribute : configs) {
                        if (authorities.contains(configAttribute)) {
                            return ACCESS_GRANTED;
                        }
                    }
                }
            }
        }
        return result;
    }
}

2. 创建SQL数据表

我们将创建一个名为 user_role 的表来存储用户角色的权限信息。

CREATE TABLE `user_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL,
  `authority` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
);

3. 向表中添加数据

我们需要在表中添加一些测试数据。

INSERT INTO `user_role` (`username`, `authority`) VALUES
('user', 'ROLE_USER'),
('admin', 'ROLE_ADMIN'),
('admin', 'ROLE_USER');

4. 创建数据源

我们需要创建一个数据源,并使用 JdbcDaoImpl 从数据库中动态加载权限信息。

<beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <beans:property name="url" value="jdbc:mysql://localhost:3306/test" />
    <beans:property name="username" value="root" />
    <beans:property name="password" value="password" />
</beans:bean>

<beans:bean id="jdbcDao" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
    <beans:property name="dataSource" ref="dataSource" />
    <beans:property name="usersByUsernameQuery" value="SELECT username,password,enabled FROM users WHERE username=?"/>
    <beans:property name="authoritiesByUsernameQuery" value="SELECT username,authority FROM user_role WHERE username=?"/>
</beans:bean>

5. 配置权限信息汇集点与角色配置

我们需要创建一个表示权限信息汇集点的 SecurityMetadataSource 并提供角色配置信息。

public class CustomSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    private Map<String, List<ConfigAttribute>> roleMap;

    public CustomSecurityMetadataSource(Map<String, List<ConfigAttribute>> roleMap) {
        this.roleMap = roleMap;
    }

    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        FilterInvocation filterInvocation = (FilterInvocation) object;
        HttpServletRequest request = filterInvocation.getHttpRequest();
        String url = request.getRequestURI().substring(request.getContextPath().length());
        List<ConfigAttribute> attributes = roleMap.get(url);
        if (attributes != null && !attributes.isEmpty()) {
            return new HashSet<>(attributes);
        }
        return null;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        Set<ConfigAttribute> set = new HashSet<>();
        for (List<ConfigAttribute> attrs : roleMap.values()) {
            set.addAll(attrs);
        }
        return set;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }

}

6. 配置访问控制

我们需要创建一个表示访问控制的 AccessDecisionManager 并将 CustomRoleVoterCustomSecurityMetadataSource 与之相互关联。

<beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
    <beans:constructor-arg>
        <beans:list>
            <beans:bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
            <beans:bean class="com.example.CustomRoleVoter">
                <beans:constructor-arg>
                    <beans:map>
                        <beans:entry key="/admin">
                            <beans:list>
                                <beans:bean class="org.springframework.security.access.SecurityConfig">
                                    <beans:constructor-arg value="ROLE_ADMIN" />
                                </beans:bean>
                                <beans:bean class="org.springframework.security.access.SecurityConfig">
                                    <beans:constructor-arg value="ROLE_USER" />
                                </beans:bean>
                            </beans:list>
                        </beans:entry>
                        <beans:entry key="/user">
                            <beans:list>
                                <beans:bean class="org.springframework.security.access.SecurityConfig">
                                    <beans:constructor-arg value="ROLE_USER" />
                                </beans:bean>
                            </beans:list>
                        </beans:entry>
                    </beans:map>
                </beans:constructor-arg>
            </beans:bean>
        </beans:list>
    </beans:constructor-arg>
</beans:bean>

<beans:bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
    <beans:property name="securityMetadataSource">
        <beans:bean class="com.example.CustomSecurityMetadataSource">
            <beans:constructor-arg>
                <beans:map>
                    <beans:entry key="/admin">
                        <beans:list>
                            <beans:bean class="org.springframework.security.access.SecurityConfig">
                                <beans:constructor-arg value="ROLE_ADMIN" />
                            </beans:bean>
                            <beans:bean class="org.springframework.security.access.SecurityConfig">
                                <beans:constructor-arg value="ROLE_USER" />
                            </beans:bean>
                        </beans:list>
                    </beans:entry>
                    <beans:entry key="/user">
                        <beans:list>
                            <beans:bean class="org.springframework.security.access.SecurityConfig">
                                <beans:constructor-arg value="ROLE_USER" />
                            </beans:bean>
                        </beans:list>
                    </beans:entry>
                </beans:map>
            </beans:constructor-arg>
        </beans:bean>
    </beans:property>
    <beans:property name="accessDecisionManager" ref="accessDecisionManager" />
    <beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>

7. 验证用户身份

最后,我们需要在Web应用程序中配置 springSecurityFilterChain 并向其中添加 FilterSecurityInterceptor

<security:http pattern="/admin/**" auto-config="false" use-expressions="false">
    <security:intercept-url pattern="/admin/**" access="ROLE_ADMIN,ROLE_USER" />
    <security:custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR" />
</security:http>

<security:http pattern="/user/**" auto-config="false" use-expressions="false">
    <security:intercept-url pattern="/user/**" access="ROLE_USER" />
    <security:custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR" />
</security:http>

<security:authentication-manager>
    <security:authentication-provider user-service-ref="jdbcDao" />
</security:authentication-manager>

<beans:bean id="springSecurityFilterChain" class="org.springframework.web.filter.DelegatingFilterProxy">
    <beans:property name="targetBeanName" value="filterSecurityInterceptor" />
</beans:bean>

示例1:验证用户访问Admin页面的权限

假设某位用户拥有 ROLE_USERROLE_ADMIN 权限,但是在访问 /admin 页面时被拒绝,因为访问此页面必须要满足 ROLE_ADMIN 权限。

我们可以使用以下方法验证账号和密码。

用户名:admin 密码:123456

示例代码如下:

@Test
public void testAccessAdminPage() throws Exception {
    MvcResult result = mockMvc.perform(get("/admin/hello").with(httpBasic("admin", "123456")))
            .andDo(print())
            .andExpect(status().isOk())
            .andReturn();
    String content = result.getResponse().getContentAsString();
    assertThat(content).isEqualTo("Hello Admin");
}

示例2:验证用户访问User页面的权限

假设某个用户只拥有 ROLE_USER 权限,那么访问 /user 页面将是允许的,但是访问 /admin 页面将被禁止。

我们可以使用以下方法验证账号和密码。

用户名:user 密码:123456

示例代码如下:

@Test
public void testAccessUserPage() throws Exception {
    MvcResult result = mockMvc.perform(get("/user/hello").with(httpBasic("user", "123456")))
            .andDo(print())
            .andExpect(status().isOk())
            .andReturn();
    String content = result.getResponse().getContentAsString();
    assertThat(content).isEqualTo("Hello User");
}

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringSecurity实现动态加载权限信息的方法 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • 浅谈.html,.htm,.shtml,.shtm的区别与联系

    下面是详细讲解“浅谈.html,.htm,.shtml,.shtm的区别与联系”的攻略: 标准的HTML文件格式 HTML(Hypertext Markup Language)是用来编写网页的标准语言,而 “.html” 或 “.htm” 文件就是标准的 HTML 文件格式。这两种格式本质上是没有区别的,只不过后缀名的不同。一些 Web 服务器或操作系统在默…

    Java 2023年6月15日
    00
  • Java中读取文件转换为字符串的方法

    想要读取文件并将其转换为字符串,可以使用Java中的流和缓冲区来实现。具体实现过程如下: 使用Java中的File类打开要读取的文件,可以使用文件的路径或URI来指定文件。例如,打开当前路径下的test.txt文件: File file = new File("test.txt"); 构造一个FileReader对象以读取文件内容。如果需…

    Java 2023年5月27日
    00
  • python中jieba库(中文分词库)使用安装教程

    下面是“Python中jieba库使用安装教程”的完整攻略。 简介 jieba是一款优秀的Python中文分词库,可实现中文文本的分词和词性标注。同时,jieba还支持自定义词典,可根据具体需求进行分词。 安装 方法一:使用pip安装 使用pip安装是比较常见的方法,可在命令行窗口中输入以下命令: pip install jieba 方法二:源码安装 使用源…

    Java 2023年5月19日
    00
  • java删除文件和文件夹具体实现

    当我们需要清理旧数据或者卸载应用程序时,通常需要删除一些文件或者文件夹。下面我来讲解一下Java中如何删除文件和文件夹的实现过程。 删除文件 Java中删除文件的方式非常简单,使用Java的File类提供的delete()方法即可。该方法有一个返回值,表示是否成功删除文件。 例如,我有一个名为test.txt的文件,它的绝对路径为C:\Users\usern…

    Java 2023年5月20日
    00
  • 使用JSP实现简单的用户登录注册页面示例代码解析

    一、JSP实现用户登录注册页面示例代码说明 1.创建一个JSP文件,命名为login.jsp,实现用户的登录页面代码。 <!DOCTYPE html> <html> <head> <title>Login Page</title> </head> <body> <h1&…

    Java 2023年6月15日
    00
  • 一不小心就让Java开发踩坑的fail-fast是个什么鬼?(推荐)

    一不小心就让Java开发踩坑的fail-fast是个什么鬼? 在Java中,有一种叫做fail-fast的机制,它主要是用于快速发现程序中的错误,并迅速抛出异常。 什么是fail-fast机制? fail-fast机制指的是集合中在进行结构性操作(增删改)时,如果集合的状态发生了变化,那么就立即抛出异常以终止当前操作,这样可以防止对集合的并发修改。 在Jav…

    Java 2023年5月25日
    00
  • 转载一个别人收藏的精典网站Ruby,HIBERNATE相关

    关于“转载一个别人收藏的精典网站Ruby,HIBERNATE相关”的完整攻略,我会按照以下步骤进行详细讲解: 1. 确定转载目的 在转载一篇文章之前,我们需要明确自己的转载目的。是为了丰富自己的博客内容,还是为了分享给更多人?这一点很重要,因为它将决定你应该如何进行转载。 2. 征求原作者许可 在转载别人的文章之前,最重要的是要获得原作者的授权,否则可能会引…

    Java 2023年5月20日
    00
  • Kafka中消息队列的两种模式讲解

    Kafka中消息队列的两种模式讲解 Apache Kafka是一个开源的分布式流处理平台,其主要功能是异步处理、发布和订阅消息。在Kafka中,消息队列的模式分为两种:点对点模式和发布/订阅模式。 点对点模式 点对点模式通常用于一个消息只能被一个消费者消费的场景,即一条消息只会被消费一次。这种模式中,消息被发送到Kafka中的一个队列中,在队列中等待消费者来…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部