spring security 动态配置权限
前言
使用HttpSecurity配置的认证授权规则还是不够灵活,无法实现资源和角色之间的动态调整,要实现动态配置URL权限,就需要我们自定义权限配置。一、数据表
menu表(定义用户能够访问的URL模式)
role表(权限表)
menu_role表(权限访问资源的绑定)
二、用到一个操作的Menu的Mapper
package com.hzw.security.mapper;
import com.hzw.security.bean.Menu;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface MenuMapper {
List<Menu> getAllMenus();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hzw.security.mapper.MenuMapper">
<resultMap id="BaseResultMap" type="com.hzw.security.bean.Menu">
<id property="id" column="id"/>
<result property="pattern" column="pattern"/>
<collection property="roles" ofType="com.hzw.security.bean.Role">
<id property="id" column="rid"/>
<result property="name" column="rname"/>
<result property="nameZh" column="rnameZh"/>
</collection>
</resultMap>
<select id="getAllMenus" resultMap="BaseResultMap">
select m.*,r.id AS rid,r.name AS rname,r.nameZh AS rnameZh from menu m left
join menu_role mr ON m.id=mr.mid left join role r ON mr.rid = r.id
</select>
</mapper>
三、自定义FilterInvocationSecurityMetadataSource
定义CustomFilterInvocationSecurityMetadataSource实现FilterInvocationSecurityMetadataSource
package com.hzw.security.component;
import com.hzw.security.bean.Menu;
import com.hzw.security.bean.Role;
import com.hzw.security.mapper.MenuMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import java.util.Collection;
import java.util.List;
@Component
public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
AntPathMatcher antPathMatcher = new AntPathMatcher();
@Autowired
MenuMapper menuMapper;
Logger logger = LoggerFactory.getLogger(getClass());
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
//提取当前请求的url
String requestUrl = ((FilterInvocation) object).getRequestUrl();
//获取所有资源信息
List<Menu> menus = menuMapper.getAllMenus();
//遍历资源信息,获取当前请求的URL所需要的角色信息
for (Menu menu:menus){
if (antPathMatcher.match(menu.getPattern(),requestUrl)){
List<Role> roles = menu.getRoles();
System.out.println(roles);
String[] roleArr = new String[roles.size()];
for (int i= 0;i<roleArr.length;i++){
roleArr[i] = roles.get(i).getName();
}
return SecurityConfig.createList(roleArr);
}
}
return SecurityConfig.createList("ROLE_LOGIN");
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
//supports方法返回类对象是否支持校验
@Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
四、自定义FilterInvocationSecurityMetadataSource
package com.hzw.security.component;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import java.util.Collection;
public class CustomAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (ConfigAttribute configAttribute:configAttributes){
if ("ROLE_LOGIN".equals(configAttribute.getAttribute())
&& authentication instanceof UsernamePasswordAuthenticationToken){
return;
}
for (GrantedAuthority authority:authorities){
if (configAttribute.getAttribute().equals(authority.getAuthority())){
return;
}
}
}
throw new AccessDeniedException("权限不足");
}
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
五、在spring security中配置如上两个自定义的类
package com.hzw.security.config;
import com.hzw.security.component.CustomAccessDecisionManager;
import com.hzw.security.component.CustomFilterInvocationSecurityMetadataSource;
import com.hzw.security.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserService userService;
//密码加密方案,如果没配置可以省略
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
protected void configure(HttpSecurity http) throws Exception {
// super.configure(http);
http.authorizeRequests().antMatchers("/").permitAll()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O object) {
object.setSecurityMetadataSource(cfisms());
object.setAccessDecisionManager(cadm());
return object;
}
});
http.formLogin().loginProcessingUrl("/login").permitAll().and().csrf().disable();
http.logout().logoutSuccessUrl("/");
//开启记住我
http.rememberMe();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
//配置各URl所需要的角色
@Bean
CustomFilterInvocationSecurityMetadataSource cfisms(){
return new CustomFilterInvocationSecurityMetadataSource();
}
//配置访问url时校验角色的权限
@Bean
CustomAccessDecisionManager cadm(){
return new CustomAccessDecisionManager();
}
}