SpringSecurity实现动态url拦截(基于rbac模型)
目录
- 1、了解主要的过滤器
- 1、SecurityMetadataSource
- 2、UserDetailsService
- 3、AccessDecisionManager
- 2、正式实战了
- 1 使用idea的Srping Initializr 创建一个项目 我的版本如下Pom.xml
- 2,创建一个springSecurity配置类,你也可以使用配置文件的方法。我这里使用了boot的配置类
- 3、自定义SecurityMetadataSource拦截器
拦截方法,会更简单一点吧 基于PermissionEvaluator 可以自行先了解
1、了解主要的过滤器
1、SecurityMetadataSource
权限资源拦截器。
有一个接口继承与它FilterInvocationSecurityMetadataSource,但FilterInvocationSecurityMetadataSource只是一个标识接口,
对应于FilterInvocation,本身并无任何内容:
主要方法:
public CollectiongetAttributes(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 方法,用来判定用户是否有此权限。如果不在权限表中则放行。@Overridepublic void decide(Authentication authentication, Object o, CollectionconfigAttributes)
2、正式实战了
1 使用idea的Srping Initializr 创建一个项目 我的版本如下Pom.xml
【SpringSecurity实现动态url拦截(基于rbac模型)】4.0.0 com.example demo0.0.1-SNAPSHOT jardemo Demo project for Spring Boot org.springframework.boot spring-boot-starter-parent1.5.9.RELEASE UTF-8UTF-8 1.8 1.8 1.8 3.2.7 1.2.2 org.springframework.boot spring-boot-starter-aoporg.springframework.boot spring-boot-starter-securityorg.springframework.boot spring-boot-starter-thymeleaforg.springframework.boot spring-boot-starter-weborg.springframework.boot spring-boot-starter-testtestorg.springframework.security spring-security-testtestorg.thymeleaf.extras thymeleaf-extras-springsecurity4org.webjars bootstrap3.3.7 mysql mysql-connector-java5.1.6 com.mchange c3p00.9.5.2 commons-logging commons-loggingorg.springframework spring-jdbcorg.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//这里启动securitypublic class SpringSecurityConfig extends WebSecurityConfigurerAdapter{@Autowired//下面会编写这个类private MyFilterSecurityInterceptor myFilterSecurityInterceptor; @Autowired//下面会编写这个类private DefaultAccessDeniedHandler defaultAccessDeniedHandler; @BeanUserDetailsService customUserService(){ //注册UserDetailsService 的beanreturn new CustomUserService(); }@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(customUserService()); //user Details Service验证}@Overrideprotected 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.*; /** */@Servicepublic class MyInvocationSecurityMetadataSourceServiceimplementsFilterInvocationSecurityMetadataSource {private HashMap> map =null; @Autowiredprivate PermissionDao permissionDao; /*** 自定义方法。最好在项目启动时,去数据库查询一次就好。* 数据库查询一次 权限表出现的所有要拦截的url*/public void loadResourceDefine(){map = new HashMap<>(); Collectionarray; ConfigAttribute cfg; //去数据库查询 使用dao层。 你使用自己的即可List permissions = permissionDao.findAll(); for(Permission permission : permissions) {array = new ArrayList<>(); //下面你可以添加你想要比较的信息过去。 注意的是,需要在用户登录时存储的权限信息一致cfg = new SecurityConfig(permission.getName()); //此处添加了资源菜单的名字,例如请求方法到ConfigAttribute的集合中去。此处添加的信息将会作为MyAccessDecisionManager类的decide的第三个参数。array.add(cfg); //用权限的getUrl() 作为map的key,用ConfigAttribute的集合作为 value,map.put(permission.getUrl(), array); }}//此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。@Overridepublic 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(FilterInvocation 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/**"); }@Overridepublic Collection getAllConfigAttributes() {return null; }@Overridepublic 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. */@Servicepublic class CustomUserService implements UserDetailsService { //自定义UserDetailsService 接口@AutowiredUserDao userDao; @AutowiredPermissionDao 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()); ListgrantedAuthorities = 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; import java.util.Collection; import java.util.Iterator; @Servicepublic class MyAccessDecisionManager implements AccessDecisionManager {// decide 方法是判定是否拥有权限的决策方法,//authentication 是释CustomUserService中循环添加到 GrantedAuthority 对象中的权限信息集合.//object 包含客户端发起的请求的requset信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest(); //configAttributes 为MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。@Overridepublic void decide(Authentication authentication, Object o, CollectionconfigAttributes) 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"); }@Overridepublic boolean supports(ConfigAttribute configAttribute) {return true; }@Overridepublic 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; @Servicepublic class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {@Autowiredprivate FilterInvocationSecurityMetadataSource securityMetadataSource; @Autowiredprivate void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {super.setAccessDecisionManager(myAccessDecisionManager); }@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic 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); }}@Overridepublic void destroy() {}@Overridepublic Class> getSecureObjectClass() {return FilterInvocation.class; }@Overridepublic SecurityMetadataSource obtainSecurityMetadataSource() {return this.securityMetadataSource; }}
运行项目就实现了。去试试吧。
记得将自定义拦截器放进security的过滤器链中。
到此这篇关于SpringSecurity实现动态url拦截(基于rbac模型)的文章就介绍到这了,更多相关SpringSecurity 动态url拦截内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
推荐阅读
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- MybatisPlus使用queryWrapper如何实现复杂查询
- python学习之|python学习之 实现QQ自动发送消息
- 孩子不是实现父母欲望的工具——林哈夫
- opencv|opencv C++模板匹配的简单实现
- Node.js中readline模块实现终端输入
- java中如何实现重建二叉树
- 人脸识别|【人脸识别系列】| 实现自动化妆
- paddle|动手从头实现LSTM
- pytorch|使用pytorch从头实现多层LSTM