SpringSecurity实现动态url拦截(基于rbac模型)

网友投稿 287 2022-12-17

SpringSecurity实现动态url拦截(基于rbac模型)

目录1、了解主要的过滤器1、SecurityMetadataSource2、UserDetailsService3、AccessDecisionManager2、正式实战了1 使用idea的Srping Initializr 创建一个项目 我的版本如下Pom.xml2,创建一个springSecurity配置类,你也可以使用配置文件的方法。我这里使用了boot的配置类3、自定义SecurityMetadataSource拦截器

后续会讲解如何实现方法拦截。其实与url拦截大同小异。

拦截方法,会更简单一点吧 基于PermissionEvaluator 可以自行先了解

1、了解主要的过滤器

1、SecurityMetadataSource

权限资源拦截器。

有一个接口继承与它FilterInvocationSecurityMetadataSource,但FilterInvocationSecurityMetadataSource只是一个标识接口,

 对应于FilterInvocation,本身并无任何内容:

主要方法:

public Collection getAttributes(Object object) throws IllegalArgumentException {

}

每一次请求url,都会调用这个方法。object存储了请求的信息。如;rul

2、UserDetailsService

用户登录,会先调用这里面的 loadUserByUsername。通过用户名去查询用户是否存在数据库。

​ 在这里面进行查询,获得用户权限信息

3、AccessDecisionManager

里面的decide方法。

// decide 方法是判定是否拥有权限的决策方法,

//authentication 是释UserService中循环添加到 GrantedAuthority 对象中的权限信息集合.

//object 包含客户端发起的请求的requset信息

,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();

//configAttributes 为MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,

此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,

用来判定用户是否有此权限。如果不在权限表中则放行。

@Override

public void decide(Authentication authentication, Object o, Collection configAttributes)

2、正式实战了

1 使用idea的Srping Initializr 创建一个项目 我的版本如下Pom.xml

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

com.example

demo

0.0.1-SNAPSHOT

jar

demo

Demo project for Spring Boot

org.springframework.boot

spring-boot-starter-parent

1.5.9.RELEASE

UTF-8

UTF-8

1.8

1.8

1.8

3.2.7

1.2.2

org.springframework.boot

spring-boot-starter-aop

org.springframework.boot

spring-boot-starter-security

org.springframework.boot

spring-boot-starter-thymeleaf

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-test

test

org.springframework.security

spring-security-test

test

org.thymeleaf.extras

thymeleaf-extras-springsecurity4

org.webjars

bootstrap

3.3.7

mysql

mysql-connector-java

5.1.6

com.mchange

c3p0

0.9.5.2

commons-logging

commons-logging

org.springframework

spring-jdbc

org.mybatis

mybatis

${mybatis.version}

org.mybatis

mybatis-spring

${mybatis-spring.version}

org.springframework.boot

spring-boot-maven-plugin

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

com.example

demo

0.0.1-SNAPSHOT

jar

demo

Demo project for Spring Boot

org.springframework.boot

spring-boot-starter-parent

1.5.9.RELEASE

UTF-8

UTF-8

1.8

1.8

1.8

3.2.7

1.2.2

org.springframework.boot

spring-boot-starter-aop

org.springframework.boot

spring-boot-starter-security

org.springframework.boot

spring-boot-starter-thymeleaf

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-test

test

org.springframework.security

spring-security-test

test

org.thymeleaf.extras

thymeleaf-extras-springsecurity4

org.webjars

bootstrap

3.3.7

mysql

mysql-connector-java

5.1.6

com.mchange

c3p0

0.9.5.2

commons-logging

commons-logging

org.springframework

spring-jdbc

org.mybatis

mybatis

${mybatis.version}

org.mybatis

mybatis-spring

${mybatis-spring.version}

org.springframework.boot

spring-boot-maven-plugin

2,创建一个springSecurity配置类,你也可以使用配置文件的方法。我这里使用了boot的配置类

package com.example.config;

import com.example.service.CustomUserService;

import com.example.service.MyFilterSecurityInterceptor;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

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.core.userdetails.UserDetailsService;

import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

@Configuration //声明为配置类

@EnableWebSecurity //这里启动security

public class SpringSecurityConfig extends WebSecurityConfigurerAdapter{

@Autowired //下面会编写这个类

private MyFilterSecurityInterceptor myFilterSecurityInterceptor;

@Autowired //下面会编写这个类

private DefaultAccessDeniedHandler defaultAccessDeniedHandler;

@Bean

UserDetailsService customUserService(){ //注册UserDetailsService 的bean

return new CustomUserService();

}

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

auth.userDetailsService(customUserService()); //user Details Service验证

}

@Override

protected void configure(HttpSecurity http) throws Exception {

http.exceptionHandling()

.accessDeniedHandler(defaultAccessDeniedHandler);

http.authorizeRequests()

.antMatchers("/css/**").permitAll()

.anyRequest().authenticated() //任何请求,登录后可以访问

.and()

.formLogin().loginPage("/login").failureUrl("/login?error").permitAll() //登录页面用户任意访问

.and()

.logout().permitAll(); //注销行为任意访问

http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);

}

}

3、自定义SecurityMetadataSource拦截器

package com.example.service;

import com.example.dao.PermissionDao;

import com.example.domain.Permission;

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.security.web.util.matcher.AntPathRequestMatcher;

import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;

import java.util.*;

/**

*/

@Service

public class MyInvocationSecurityMetadataSourceService implements

FilterInvocationSecurityMetadataSource {

private HashMap> map =null;

@Autowired

private PermissionDao permissionDao;

/**

* 自定义方法。最好在项目启动时,去数据库查询一次就好。

* 数据库查询一次 权限表出现的所有要拦截的url

*/

public void loadResourceDefine(){

map = new HashMap<>();

Collection array;

ConfigAttribute cfg;

//去数据库查询 使用dao层。 你使用自己的即可

List permissions = permissionDao.findAll();

for(Permission permission : permissions) {

array = new ArrayList<>();

//下面你可以添加你想要比较的信息过去。 注意的是,需要在用户登录时存储的权限信息一致

cfg = new SecurityConfig(permission.getName());

qGlItv //此处添加了资源菜单的名字,例如请求方法到ConfigAttribute的集合中去。此处添加的信息将会作为MyAccessDecisionManager类的decide的第三个参数。

array.add(cfg);

//用权限的getUrl() 作为map的key,用ConfigAttribute的集合作为 value,

map.put(permission.getUrl(), array);

}

}

//此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。

@Override

public Collection getAttributes(Object object) throws IllegalArgumentException {

FilterInvocation filterInvocation = (FilterInvocation) object;

String fullRequestUrl = filterInvocation.getFullRequestUrl();

//若是静态资源 不做拦截 下面写了单独判断静态资源方法

if (isMatcherAllowedRequest(filterInvocation)) {

System.out.println("我没有被拦截"+fullRequestUrl);

return null;

}

//map 为null 就去数据库查

if(map ==null) {

loadResourceDefine();

}

//测试 先每次都查

//object 中包含用户请求的request 信息

HttpServletRequest request = filterInvocation.getHttpRequest();

AntPathRequestMatcher matcher;

String resUrl;

for(Iterator iter = map.keySet().iterator(); iter.hasNext(); ) {

resUrl = iter.next();

matcher = new AntPathRequestMatcher(resUrl);

if(matcher.matches(request)) {

return map.get(resUrl);

}

}

return null;

}

/**

* 判断当前请求是否在允许请求的范围内

* @param fi 当前请求

* @return 是否在范围中

*/

private boolean isMatcherAllowedRequest(FilterInvocationhttp:// fi){

return allowedRequest().stream().map(AntPathRequestMatcher::new)

.filter(requestMatcher -> requestMatcher.matches(fi.getHttpRequest()))

.toArray().length > 0;

}

/**

* @return 定义允许请求的列表

*/

private List allowedRequest(){

return Arrays.asList("/login","/css/**","/fonts/**","/js/**","/scss/**","/img/**");

}

@Override

public Collection getAllConfigAttributes() {

return null;

}

@Override

public boolean supports(Class> clazz) {

return true;

}

}

自定义UserDetailsService 。登录的时候根据用户名去数据库查询用户拥有的权限信息

package com.example.service;

import com.example.dao.PermissionDao;

import com.example.dao.UserDao;

import com.example.domain.Permission;

import com.example.domain.SysRole;

import com.example.domain.SysUser;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.authority.SimpleGrantedAuthority;

import org.springframework.security.core.userdetails.User;

import org.springframework.security.core.userdetails.UserDetails;

import org.springframework.security.core.userdetails.UserDetailsService;

import org.springframework.security.core.userdetails.UsernameNotFoundException;

import org.springframework.stereotype.Service;

import java.util.ArrayList;

import java.util.List;

/**

* Created by yangyibo on 17/1/18.

*/

@Service

public class CustomUserService implements UserDetailsService { //自定义UserDetailsService 接口

@Autowired

UserDao userDao;

@Autowired

PermissionDao permissionDao;

public UserDetails loadUserByUsername(String username) {

SysUser user = userDao.findByUserName(username);

for (SysRole role : user.getRoles()) {

System.out.println(role.getName());

}

if (user != null) {

//根据用户id 去查找用户拥有的资源

List permissions = permissionDao.findByAdminUserId(user.getId());

List grantedAuthorities = new ArrayList <>();

for (Permission permission : permissions) {

if (permission != null && permission.getName()!=null) {

GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getName());

//1:此处将权限信息添加到 GrantedAuthority 对象中,在后面进行全权限验证时会使用GrantedAuthority 对象。

grantedAuthorities.add(grantedAuthority);

}

}

return new User(user.getUsername(), user.getPassword(), grantedAuthorities);

} else {

throw new UsernameNotFoundException("admin: " + username + " do not exist!");

}

}

}

自定义AccessDecisionManager

package com.example.service;

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.core.Authentication;

import org.springframework.security.core.GrantedAuthority;

import org.springframework.stereotype.Service;

qGlItv

import java.util.Collection;

import java.util.Iterator;

@Service

public class MyAccessDecisionManager implements AccessDecisionManager {

// decide 方法是判定是否拥有权限的决策方法,

//authentication 是释CustomUserService中循环添加到 GrantedAuthority 对象中的权限信息集合.

//object 包含客户端发起的请求的requset信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();

//configAttributes 为MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。

@Override

public void decide(Authentication authentication, Object o, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {

if (null == configAttributes || configAttributes.size() <= 0 ) {

return;

}

ConfigAttribute c;

String needRole;

for (Iterator iter = configAttributes.iterator(); iter.hasNext(); ) {

c = iter.next();

needRole = c.getAttribute();

for (GrantedAuthority ga : authentication.getAuthorities()) { //authentication 为在注释1 中循环添加到 GrantedAuthority 对象中的权限信息集合

if (needRole.trim().equals(ga.getAuthority())) {

return;

}

}

}

throw new AccessDeniedException("no right");

}

@Override

public boolean supports(ConfigAttribute configAttribute) {

return true;

}

@Override

public boolean supports(Class> aClass) {

return true;

}

}

自定义拦截器

package com.example.service;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.security.access.SecurityMetadataSource;

import org.springframework.security.access.intercept.AbstractSecurityInterceptor;

import org.springframework.security.access.intercept.InterceptorStatusToken;

import org.springframework.security.web.FilterInvocation;

import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;

import org.springframework.stereotype.Service;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import java.io.IOException;

@Service

public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {

@Autowired

private FilterInvocationSecurityMetadataSource securityMetadataSource;

@Autowired

private void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {

super.setAccessDecisionManager(myAccessDecisionManager);

}

@Override

public void init(FilterConfig filterConfig) throws ServletException {

}

@Override

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);

invoke(fi);

}

public void invoke(FilterInvocation fi) throws IOException, ServletException {

//fi里面有一个被拦截的url

//里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限

//再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够

InterceptorStatusToken token = super.beforeInvocation(fi);

try {

//执行下一个拦截器

fi.getChain().doFilter(fi.getRequest(), fi.getResponse());

} finally {

super.afterInvocation(token, null);

}

}

@Override

public void destroy() {

}

@Override

public Class> getSecureObjectClass() {

return FilterInvocation.class;

}

@Override

public SecurityMetadataSource obtainSecurityMetadataSource() {

return this.securityMetadataSource;

}

}

运行项目就实现了。去试试吧。

记得将自定义拦截器放进security的过滤器链中。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java Assert.assertEquals案例详解
下一篇:Activiti7整合Springboot使用记录
相关文章

 发表评论

暂时没有评论,来抢沙发吧~