前言

要想实现动态配置URL权限,就要自定义权限配置

数据库

那总的来说,大概是怎么一个流程呢?
首先先创建对应数据表Bean

创建Bean

public class Role {private Integer id;private String name;private String nameZh;//省略getter setter
}
public class Menu {private Integer id;private String pattern;private List<Role> roles;
//省略gettet setter
}
public class User implements UserDetails {private Integer id;private String username;private String password;private Boolean enabled;private Boolean locked;private List<Role> roles;  /*这里装着该用户拥有的角色,一般情况,每一个用户都有对应一个或者几个角色,当然一开始时空的,所以要去数据库查询,并赋值给这个集合, 方便security进行比对判断。*//*** 获取登录过后用户所拥有的角色信息* @return 角色信息集合*/@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {//role集合不能直接给security拿去用,所以创建他能用的对象的集合,将roles集合里的对象放进去List<SimpleGrantedAuthority> authorities = new ArrayList<>();for (Role r : roles){authorities.add(new SimpleGrantedAuthority(r.getName()));}return authorities;}@Overridepublic String getPassword() {return password;  //这里的密码会给security来比对前端传过来的密码}@Overridepublic String getUsername() {return username;}/*** 账户是否未过期* @return 返回enable属性*/@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return !locked;}/*** 密码是否未过期* @return Boolean*/@Overridepublic boolean isCredentialsNonExpired() {return true;}/*** 账户是否可用* @return*/@Overridepublic boolean isEnabled() {return enabled;}//省略getter setter
}

创建持久层接口

MenuMapper.xml
这个mapper的getAllMeus方法用来获取每一个url对应需要的一个或几个角色

<mapper namespace="pljandsyx.top.securitydy.mapper.MenuMapper"><resultMap id="BaseResultMap" type="pljandsyx.top.securitydy.bean.Menu"><id property="id" column="id"/><result property="pattern" column="pattern"/><collection property="roles" ofType="pljandsyx.top.securitydy.bean.Role"><id property="id" column="rid"/><result property="name" column="rname"/><result property="nameZh" column="rnameZh"/></collection></resultMap><select id="getAllMeus" resultMap="BaseResultMap">SELECTm.* , r.id AS rid ,r.`name` AS rname , r.nameZh AS rnameZhFROMmenu as mLEFT JOINmenu_role as mr ON m.id = mr.midLEFT JOINrole as r ON r.id = mr.rid</select>
</mapper>

MenuMapper.java

@Repository
public interface MenuMapper {List<Menu> getAllMeus();
}

UserMapper.xml
这个方法就个就更简单了,根据用户的id获取用户所具有的角色

<mapper namespace="pljandsyx.top.securitydy.mapper.UserMapper"><select id="loadUserByUsername" parameterType="String" resultType="pljandsyx.top.securitydy.bean.User">select * from user where username= #{username}</select><select id="getRoleById" parameterType="Integer" resultType="pljandsyx.top.securitydy.bean.Role">select * from role where id in (select rid from user_role where uid = #{id})</select>
</mapper>

UserMapper.java

@Repository
public interface UserMapper {User loadUserByUsername(String username);List<Role> getRoleById(Integer id);
}

创建Service层

UserService:

@Service
public class UserService implements UserDetailsService {@AutowiredUserMapper userMapper;/*** 验证用户,登录的时候用到* @param username 表单输入的用户名* @return 返回user对象* @throws UsernameNotFoundException 用户不存在异常*/@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user =  userMapper.loadUserByUsername(username);//查询用户是否存在,同时查出来的用户是带有加密的密码,后面会配置securityConfig来比对前端传来的密码以及配置加密规则if (user==null){throw new UsernameNotFoundException("用户不存在");}user.setRoles(userMapper.getRoleById(user.getId()));//给查询到的用户赋予查询到的他应有的角色return user;}
}

MenuService

@Service
public class MenuService{@AutowiredMenuMapper menuMapper;public List<Menu> getAllMeus(){List<Menu> menuList =  menuMapper.getAllMeus();return menuList;}
}

配置

创建实现AccessDecisionManager接口的实现类CustomAccessDecisionManager:
该实现类的decide()根据当前登录用户的角色信息,跟访问当前URL所需要的角色进行对比

@Component
public class CustomAccessDecisionManager implements AccessDecisionManager {/*** 该方法判断当前登录用户是否具备角色信息* @param authentication 当前登录用户的信息* @param o FilterInvocation对象,可以获得请求对象* @param collection  FilterInvocationSecurityMetadataSourceImpl类中获取的-》当前URL需要的角色信息* @throws AccessDeniedException 不具备角色信息* @throws InsufficientAuthenticationException*/@Overridepublic void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();//获取当前登录用户的角色信息for (ConfigAttribute  configAttribute : collection){//遍历当前URL需要的角色信息//如果此URL需要"ROLE_login角色,并且用户已经登陆才,放行"if ("ROLE_login".equals(configAttribute.getAttribute()) && authentication instanceof UsernamePasswordAuthenticationToken){//空返回,跳出方法return;}//遍历当前登录用户的角色信息,如果有与所需角色信息时,跳出判断for (GrantedAuthority authority : authorities){if (configAttribute.getAttribute().equals(authority.getAuthority())){return;}}}//判断失败,抛出异常throw new AccessDeniedException("权限不足");}@Overridepublic boolean supports(ConfigAttribute configAttribute) {return true;}@Overridepublic boolean supports(Class<?> aClass) {return true;}
}

创建实现FilterInvocationSecurityMetadataSource接口的实现类FilterInvocationSecurityMetadataSourceImpl,实现类最重要的方法是getAttributes(),用获取当前URL访问所需要的角色集合

@Component
public class FilterInvocationSecurityMetadataSourceImpl implements FilterInvocationSecurityMetadataSource {//用来实现ant风格的URL匹配AntPathMatcher antPathMatcher = new AntPathMatcher();@AutowiredMenuService menuService;/*** 返回当前URL访问所需要的角色信息* @param o  该参数是FilterInvocation ,可以用来获取URL* @return Collection<ConfigAttribute> 表示当前请求URL所需要的角色* @throws IllegalArgumentException*/@Overridepublic Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {String requestUrl = ((FilterInvocation) o).getRequestUrl(); //获取请求URLList<Menu> allMeus = menuService.getAllMeus();//从数据库中获取所有匹配规则for (Menu menu : allMeus){//遍历所有的匹配规则if (antPathMatcher.match(menu.getPattern(),requestUrl)){//如果与当前的URL与匹配规则符合List<Role> roleList = menu.getRoles();//获取访问URL所需要的角色String[] roles =new String[roleList.size()];//创建存储所需要角色的数据for (int i = 0; i < roleList.size(); i++) {//遍历数组并将刚才所需要的角色存入数组roles[i] = roleList.get(i).getName();}return SecurityConfig.createList(roles);//返回数组}}//如果遍历所有menu后依然没有匹配到规则,那么就直接返回"ROLE_login";也就是登录了就可以访问的标志return SecurityConfig.createList("ROLE_login");}/*** 返回所有定义好的权限资源,如果不需要检验直接返回null即可* @return*/@Overridepublic Collection<ConfigAttribute> getAllConfigAttributes() {return null;}/*** 返回 类对象是否支持校验* @param aClass* @return*/@Overridepublic boolean supports(Class<?> aClass) {return true;}
}

最后在SpringSecurity配置文件注入配置

@Configuration
public class SecurityConfig  extends WebSecurityConfigurerAdapter {@AutowiredUserService userService;@AutowiredFilterInvocationSecurityMetadataSourceImpl filterInvocationSecurityMetadataSource;@AutowiredCustomAccessDecisionManager customAccessDecisionManager;/*** 加密* @return*/@BeanPasswordEncoder passwordEncoder(){return  new BCryptPasswordEncoder();}/*** 注入配置好UserService验证* @param auth 角色验证* @throws Exception 捕捉任何异常*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userService);}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {@Overridepublic <O extends FilterSecurityInterceptor> O postProcess(O o) {o.setSecurityMetadataSource(filterInvocationSecurityMetadataSource);o.setAccessDecisionManager(customAccessDecisionManager);return o;}}).and().formLogin().permitAll().and().csrf().disable();}
}

踩坑:

User类里没有成功获取roles集合信息,导致后续权限判断失败,(返回的authorities.size()为0),
这里注意导入的Role类是你的自定义Bean,有一个系统Role类,导错包。

/*** 获取登录过后用户所拥有的角色信息* @return 角色信息集合*/@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {List<SimpleGrantedAuthority> authorities = new ArrayList<>();for (Role r : roles){authorities.add(new SimpleGrantedAuthority(r.getName()));}return authorities;}

笔记

SpringSecurity动态权限笔记相关推荐

  1. 三、SpringSecurity 动态权限访问控制

    简介 在先前文章中我们搭建了SpringSecurity项目,并且讲解了自定义登录方式需要做哪些工作,如果你感兴趣可以前往博客阅读文章以及代码,在本文将继续讲解如何实现动态权限控制. 代码仓库:Git ...

  2. android 动态权限申请源码,Android6.0动态权限笔记

    参考: 提示用户授予或拒绝权限的系统对话框. 一. 权限说明: 1. 权限种类: Android中权限分为正常权限(即,不会对用户隐私或设备操作造成很大风险的权限)和危险权限(即,可能影响用户隐私或设 ...

  3. SpringBoot+SpringSecurity+RBAC+JWT实现动态权限框架

    一.创建数据库表 DROP TABLE IF EXISTS luo_admin; CREATE TABLE luo_admin ( id bigint(20) NOT NULL AUTO_INCREM ...

  4. android插件做动态权限,Mui本地打包笔记(四)Android自定义插件的配置(以动态申请权限为例)...

    通过自定义插件方式实现Android平台的动态申请权限功能 在上一章中完成了在Mui中调用Android原生的动态权限请求功能(Android动态申请权限的问题).虽然说完成了功能,但是在使用上并不是 ...

  5. SpringBoot 整合 Shiro 实现动态权限加载更新+ Session 共享 + 单点登录

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源: juejin.im/post/5d087d60518825 ...

  6. SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例(转)...

    SpringBoot整合mybatis.shiro.redis实现基于数据库的细粒度动态权限管理系统实例 shiro 目录(?)[+] 前言 表结构 maven配置 配置Druid 配置mybatis ...

  7. RxJava RxPermissions 动态权限 简介 原理 案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  8. SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例...

    SpringBoot整合mybatis.shiro.redis实现基于数据库的细粒度动态权限管理系统实例 shiro 目录(?)[+] 1.前言 本文主要介绍使用SpringBoot与shiro实现基 ...

  9. SpringBoot 整合Shiro实现动态权限加载更新+Session共享+单点登录

    作者:Sans_ juejin.im/post/5d087d605188256de9779e64 一.说明 Shiro是一个安全框架,项目中主要用它做认证,授权,加密,以及用户的会话管理,虽然Shir ...

最新文章

  1. 盘点:2020年最酷的12家机器学习初创公司
  2. jsp窗口关闭的触发函数
  3. android progressbar 不显示_Android多线程技术选型最全指南(1)
  4. 1、数据库设计的基本步骤
  5. JavaScript中获取表单信息并添加在表格中
  6. php隐藏文件链接,php隐藏文件实际下载地址的方法
  7. 人脸识别撞脸名画_与名画“撞脸”火爆数博会 观众直呼“太好玩”【高清组图】...
  8. Factory Method模式的误区:Factory Method模式是简化版的Abstract Factory吗?
  9. 以太坊java接口_java以太坊库web3j文档
  10. Java架构师知识体系汇总
  11. 第五十六题(最长公共子串)
  12. 进程同步与互斥:POSIX有名信号量
  13. ionic 刷新页面的几种方法
  14. Oracle for update skip locked 详解
  15. Android开发使用Glide获取图片背景色淡绿色解决办法
  16. Codeforces.838D.Airplane Arrangements(思路)
  17. 软工复习一万字资料大全总结超强无敌版稳过乱过
  18. bzoj1754: [Usaco2005 qua]Bull Math
  19. 构建之法10,11,12章的读后感
  20. 【4天快速入门Python数据挖掘之第1天】Matplotlib的使用

热门文章

  1. 超链接QTable实现
  2. shell脚本是用来做什么的
  3. SweetAlert 2 全网最详细的使用方法
  4. 使用go语言编译部署最新版Yearning【v3.0.1】
  5. 2021年oppo哲库数字IC岗位手撕代码真题(含:握手信号、自动售卖机、序列发生器、根据RTL写verilog)
  6. java调用jasperreport_Java代码导出Jasperreport
  7. java1.8.0 jce下载,关于jar:Java错误:请安装JCE无限强度管辖权策略文件
  8. 中文影评分类的神经网络模型
  9. 1620 NOIP2012 质因数分解(LOJ10198 LUOGU1075 入门) 求出[1,44722]区间内的质数
  10. ConcurrentHashMap原理分析