前言

要想实现动态配置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. mac上使用crontab周期性执行python脚本
  2. 分布式文件系统Fastdfs 详细安装笔记
  3. NEU 1040 Count
  4. 区块链架构、跨链和演进
  5. Kafka简介、安装
  6. 【Java中级篇】使用zxing生成二维码
  7. Vmware安装CentOS7后访问不了外网
  8. opencv之绘制多边形----cv2.polylines, cv2.fillPoly
  9. 集群ddos_《DNS攻击防范科普系列2》 -DNS服务器怎么防DDoS攻击
  10. Atititcmd cli环境变量的调用设置与使用
  11. 慕课软件质量保证与测试(总目录)
  12. 将win10家庭版、教育版系统激活为win10专业版
  13. 【编译原理】自下而上语法分析(C/C++源码+实验报告)
  14. ruoyi 项目启动步骤
  15. 教你如何在Windows XP使用定时关机命令
  16. 我的地盘ol位置服务器拒绝怎么办,微信我的地盘ol等级划分详解 微信我的地盘ol等级怎么分...
  17. Neural Approaches to Conversational AI Question Answering(问答,任务型对话,闲聊)
  18. c++实现高速缓存Cache
  19. adobe flash player已过期
  20. 2019年市面上360全景相机大比拼 Insta360 Pro 2,得图Detu F4 Plus,理光Theta SC,GoPro Fusion,Nikon尼康KeyMission 360

热门文章

  1. Win32应用程序是什么
  2. RASNET EDCF
  3. 关于es6中Proxy的学习笔记
  4. 【SSLGZ 1618】剑鱼行动
  5. Slime Meshregistry 开源,化解服务网格多注册中心兼容之痛
  6. 计算机底层:储存器的性能指标(CPU和内存等硬件的性能以及 对比标准)
  7. 【跟风转一发】清华差生10年奋斗经历
  8. 蒟蒻的线段树入门模板笔记
  9. 微信企业号授权(含代码)
  10. 反派经典台词(暴笑)