实现动态加载权限信息的方法是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
并将 CustomRoleVoter
和 CustomSecurityMetadataSource
与之相互关联。
<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_USER
和 ROLE_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技术站