SpringSecurity动态权限笔记
前言
要想实现动态配置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动态权限笔记相关推荐
- 三、SpringSecurity 动态权限访问控制
简介 在先前文章中我们搭建了SpringSecurity项目,并且讲解了自定义登录方式需要做哪些工作,如果你感兴趣可以前往博客阅读文章以及代码,在本文将继续讲解如何实现动态权限控制. 代码仓库:Git ...
- android 动态权限申请源码,Android6.0动态权限笔记
参考: 提示用户授予或拒绝权限的系统对话框. 一. 权限说明: 1. 权限种类: Android中权限分为正常权限(即,不会对用户隐私或设备操作造成很大风险的权限)和危险权限(即,可能影响用户隐私或设 ...
- SpringBoot+SpringSecurity+RBAC+JWT实现动态权限框架
一.创建数据库表 DROP TABLE IF EXISTS luo_admin; CREATE TABLE luo_admin ( id bigint(20) NOT NULL AUTO_INCREM ...
- android插件做动态权限,Mui本地打包笔记(四)Android自定义插件的配置(以动态申请权限为例)...
通过自定义插件方式实现Android平台的动态申请权限功能 在上一章中完成了在Mui中调用Android原生的动态权限请求功能(Android动态申请权限的问题).虽然说完成了功能,但是在使用上并不是 ...
- SpringBoot 整合 Shiro 实现动态权限加载更新+ Session 共享 + 单点登录
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源: juejin.im/post/5d087d60518825 ...
- SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例(转)...
SpringBoot整合mybatis.shiro.redis实现基于数据库的细粒度动态权限管理系统实例 shiro 目录(?)[+] 前言 表结构 maven配置 配置Druid 配置mybatis ...
- RxJava RxPermissions 动态权限 简介 原理 案例 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例...
SpringBoot整合mybatis.shiro.redis实现基于数据库的细粒度动态权限管理系统实例 shiro 目录(?)[+] 1.前言 本文主要介绍使用SpringBoot与shiro实现基 ...
- SpringBoot 整合Shiro实现动态权限加载更新+Session共享+单点登录
作者:Sans_ juejin.im/post/5d087d605188256de9779e64 一.说明 Shiro是一个安全框架,项目中主要用它做认证,授权,加密,以及用户的会话管理,虽然Shir ...
最新文章
- mac上使用crontab周期性执行python脚本
- 分布式文件系统Fastdfs 详细安装笔记
- NEU 1040 Count
- 区块链架构、跨链和演进
- Kafka简介、安装
- 【Java中级篇】使用zxing生成二维码
- Vmware安装CentOS7后访问不了外网
- opencv之绘制多边形----cv2.polylines, cv2.fillPoly
- 集群ddos_《DNS攻击防范科普系列2》 -DNS服务器怎么防DDoS攻击
- Atititcmd cli环境变量的调用设置与使用
- 慕课软件质量保证与测试(总目录)
- 将win10家庭版、教育版系统激活为win10专业版
- 【编译原理】自下而上语法分析(C/C++源码+实验报告)
- ruoyi 项目启动步骤
- 教你如何在Windows XP使用定时关机命令
- 我的地盘ol位置服务器拒绝怎么办,微信我的地盘ol等级划分详解 微信我的地盘ol等级怎么分...
- Neural Approaches to Conversational AI Question Answering(问答,任务型对话,闲聊)
- c++实现高速缓存Cache
- adobe flash player已过期
- 2019年市面上360全景相机大比拼 Insta360 Pro 2,得图Detu F4 Plus,理光Theta SC,GoPro Fusion,Nikon尼康KeyMission 360