SpringSecurity自定义权限管理
SpringSecurity环境
pom.xml
pom.xml依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- SpringSecurity的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
SpringSecurity自定义权限管理
自带的权限管理
在configure方法中设置路径对应的角色
.antMatchers("/admin/**").hasRole("ADMIN")
在另一个configure方法中设置用户及其角色
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//新版本必须对密码进行加密,设置加密算法
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
//注册用户:用户名,密码(进行对应的加密),用户的角色,可以对应多个角色
//以后可以将用户的信息从数据库中读出,这里做演示
.withUser("admin").password(new BCryptPasswordEncoder().encode("1234")).roles("ADMIN","ANOTHER")
.and()
.withUser("user").password(new BCryptPasswordEncoder().encode("1234")).roles();
}
这个测试之前说过就不说了
现在重要的是如何自定义权限管理,如何从数据库中读取用户的权限,路径对应的权限
自定义权限管理
权限资源获取类 用于获取对应路径的权限
@Component
public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
//这边只做了简单的处理
//可以从参数object中获取请求的url这里就可以读数据库得到对应的角色权限
//这里写死了全部的路径都要ROLE_ADMIN
List<ConfigAttribute> list = new ArrayList<>();
list.add(new ConfigAttribute() {
@Override
public String getAttribute() {
return "ROLE_ADMIN";
}
});
return list;
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
上面代码可以从参数object中获取请求的url这里就可以读数据库得到对应的角色权限
可以参照下面这个
@Autowired
private PermissionService permissionService;
//url对应的权限角色集合
//key - url
//value - 有权限的角色集合
private static HashMap<String,Collection<ConfigAttribute>> map =null;
private void getResourcePermission(){
map = new HashMap<>();
//获取全部的权限
List<PermissionDO> permissions = permissionService.findAllPermission();
//遍历全部权限,把url和角色列表加入map
for(PermissionDO permission : permissions){
map.put(permission.getUrl(),permission.getRoles());
}
}
//判定url是否在权限中(即url需要那种权限)
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
//如果权限map不存在获取权限
if (map == null){
getResourcePermission();
}
//获取拦截的url
HttpServletRequest request = ((FilterInvocation)object).getHttpRequest();
Iterator<String> iterator = map.keySet().iterator();
//遍历权限map寻找对应url所需的角色
while(iterator.hasNext()){
String url = iterator.next();
if(new AntPathRequestMatcher(url).matches(request)){
//url在权限表中返回给AccessDecisionManager的decide方法
return map.get(url);
}
}
return null;
}
认证管理器类 这个类是进行授权的类 会对比用户有的权限和路径需要的权限 进行判别
@Component
public class MyAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
//如果请求的资源不需要权限,则直接放行
if(configAttributes == null || configAttributes.size() <= 0) {
return;
}
//判断用户所拥有的权限是否是资源所需要的权限之一,如果是则放行,否则拦截
Iterator<ConfigAttribute> iter = configAttributes.iterator();
while(iter.hasNext()) {
String needRole = iter.next().getAttribute();
for(GrantedAuthority grantRole : authentication.getAuthorities()) {
if(needRole.trim().equals(grantRole.getAuthority().trim())) {
return;
}
}
}
throw new AccessDeniedException("no privilege");
}
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
权限拦截器类 这个类就是要进行配置的
上面两个需要注入进来
@Component
public class MyFilterSecurityInterceptor extends FilterSecurityInterceptor {
@Autowired
private FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource;
@Autowired
public void setAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
super.setAccessDecisionManager(myAccessDecisionManager);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
//这里进行拦截的处理
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
/**
* 执行下一个拦截器
*/
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
@Override
public Class<?> getSecureObjectClass() {
return FilterInvocation.class;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.filterInvocationSecurityMetadataSource;
}
}
进行过滤器配置
http.addFilterAt(filterSecurityInterceptor,FilterSecurityInterceptor.class)
.exceptionHandling()
//配置授权失败处理器,权限不足
.accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
String message = "无权查看";
PrintWriter printWriter = response.getWriter();
printWriter.write(message);
printWriter.flush();
printWriter.close();
}
});
使用postman测试
admin用户
普通用户
一个细节
权限资源获取类中给的角色是 “ROLE_ADMIN”
而配置类configure方法中的角色是”ADMIN”
这是因为自带的添加用户的会增加“ROLE_前缀”而自定义的不会
还有自带的设置路径权限的也不需要加ROLE_
//设置路径和权限
.antMatchers("/admin/**").hasRole("ADMIN")
如果我们自定义用户的获取就会不一样
@Component
public class MyUserDetailService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Collection<GrantedAuthority> collection = new ArrayList<>();
collection.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
//如果是admin用户
if (username.equals("admin"))
return new User("admin",new BCryptPasswordEncoder().encode("1234"),collection);
collection.clear();
//不是admin用户
collection.add(new SimpleGrantedAuthority("ROLE_NO"));
return new User("user",new BCryptPasswordEncoder().encode("1234"),collection);
}
}
上面这个方法中可以改为从数据库中获取角色的所有信息
这里我们是自定义的所以要自带ROLE_前缀
配置类需要修改为下面这样
@Qualifier("myUserDetailService")
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
SpringSecurity到这就结束了。