Spring Boot +Shiro 用户角色权限设计
来源:互联网 发布:手机动漫制作软件 编辑:程序博客网 时间:2024/05/17 03:13
首先创建 用户-角色-权限 三个实体类 和 用户与角色关系表和 角色与权限关系表
- 用户表UserInfo:在用户表中保存了用户的基本信息,账号、密码、姓名等;
- 权限表SysPermission(资源+控制权限):这个表中主要是保存了用户的URL地址,权限信息;
- 角色表SysRole:在这个表重要保存了系统存在的角色;
关联表:用户-角色管理表SysRoleUser(用户在系统中都有什么角色,比如admin,manage等),角色-权限关联表SysRolePermission(每个角色对应什么权限可以进行操作)。
用户实体
@TableName("user_info")public class UserInfo extends BaseEntity<UserInfo> { private static final long serialVersionUID = 1L; private Long id;//用户id; private String name;//名称(昵称或者真实姓名,不同系统不同定义) private String password; //密码; private String salt;//加密密码的盐 private Integer state;//用户状态,0:创建未认证(比如没有激活,没有输入验证码等等)--等待验证的用户 , 1:正常状态,2:用户被锁定. @TableField("user_name") private String userName; //账号. @TableField(exist=false) private List<SysRole> roleList;// 一个用户具有多个角色 }
角色实体
@TableName("sys_role")public class SysRole extends BaseEntity<SysRole> { private static final long serialVersionUID = 1L; private String available;// 是否可用,如果不可用将不会添加给用户 private String description; // 角色描述,UI界面显示使用 private Long id;// 编号 private String role; // 角色标识程序中判断使用,如"admin",这个是唯一的: @TableField(exist = false) private List<SysPermission> permissions;// 角色 - 权限关系定义; }
权限实体
@TableName("sys_permission")public class SysPermission extends BaseEntity<SysPermission> { private static final long serialVersionUID = 1L; private Long id; private String name; @TableField("parent_id") private Integer parentId; @TableField("parent_ids") private String parentIds; private String permission; @TableField("resource_type") private String resourceType; private String url; }
对应关系实体(mybatisPlus生成)
@TableName("sys_role_user")public class SysRoleUser extends Model<SysRoleUser> { private static final long serialVersionUID = 1L; private Long id; private Long roleId; private Long uid;}@TableName("sys_role_permission")public class SysRolePermission extends Model<SysRolePermission> { private static final long serialVersionUID = 1L; private Integer id; private Long permissionId; private Long roleId; }
部分接口mapper.XML不展示
<resultMap id="userInfoWithRole" type="com.core.shiro.entity.UserInfo" extends="BaseResultMap"> <!--<collection property="roleList" resultMap="com.core.shiro.mapper.SysRoleMapper.BaseResultMap"></collection>--> <collection property="roleList" ofType="com.core.shiro.entity.SysRole" > <id column="rid" property="id" /> <result column="create_time" property="createTime" /> <result column="creator" property="creator" /> <result column="edit_time" property="editTime" /> <result column="editor" property="editor" /> <result column="is_del" property="isDel" /> <result column="available" property="available" /> <result column="description" property="description" /> <result column="role" property="role" /> </collection><!-- 一对多配置 --><select id="selectUserByUserNameWithRole" resultMap="userInfoWithRole" parameterType="java.lang.String"> SELECT u.*, r.id as rid, r.ROLE, r.description, r.available FROM user_info u,sys_role r,sys_role_user ru <where> u.user_name=#{userName} and u.id=ru.uid and r.id=ru.role_id and r.is_del='f' </where> </select><resultMap id="RoleWithPermission" type="com.core.shiro.entity.SysRole" extends="BaseResultMap"> <collection property="permissions" ofType="com.core.shiro.entity.SysPermission"> <id column="pid" property="id" /> <result column="name" property="name" /> <result column="parent_id" property="parentId" /> <result column="parent_ids" property="parentIds" /> <result column="permission" property="permission" /> <result column="resource_type" property="resourceType" /> <result column="url" property="url" /> </collection> <!-- 通用查询映射结果 --> </resultMap> <select id="selectRoleByIdWithPermission" resultMap="RoleWithPermission" parameterType="java.lang.Long"> SELECT r.*, p.id as pid, P . NAME, P .parent_id, P .parent_ids, P .permission, P .resource_type, P .url from sys_permission p,sys_role r,sys_role_permission rp <where> r.id=#{id} AND r.id=rp.role_id AND p.id=rp.permission_id AND p.is_del='f' </where> </select>
使用shiro框架 大概需要三个步骤。
注入ShiroFilterFactoryBean
- 主要功能 自己编写的过滤器 可以添加到ShiroFilterFactoryBean
- 添加过滤权限规则
- 设置登录url和无权限url
注入securityManager
- 将我们自己实现的Realm设置到securityManager中。
- 注入管理器到securityManager中(比如 缓存管理器、记住我管理器)。
@Bean public DefaultWebSecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //设置realm. securityManager.setRealm(myShiroRealm()); //注入缓存管理器; securityManager.setCacheManager(ehCacheManager());//这个如果执行多次,也是同样的一个对象; //注入记住我管理器; securityManager.setRememberMeManager(rememberMeManager()); return securityManager; }
3.实现Realm继承AuthorizingRealm然后重写两个方法
- 身份认证(重写doGetAuthenticationInfo)
- 权限控制(重写doGetAuthorizationInfo)
ShiroConfiguration.java
@Configurationpublic class ShiroConfiguration { /** * ShiroFilterFactoryBean 处理拦截资源文件问题。 * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在 * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager * Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过 3、部分过滤器可指定参数,如perms,roles * */ @Bean public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){ System.out.println("ShiroConfiguration.shirFilter()"); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();//获取filters filters.put("authc", new CustomFormAuthenticationFilter());//将自定义 的FormAuthenticationFilter注入shiroFilter中 // 必须设置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //拦截器. Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>(); //配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了 filterChainDefinitionMap.put("/logout", "logout"); //配置记住我或认证通过可以访问的地址 filterChainDefinitionMap.put("/index", "user"); filterChainDefinitionMap.put("/", "user"); //验证码可以匿名访问 filterChainDefinitionMap.put("/getValidateCode", "anon"); filterChainDefinitionMap.put("/static/**", "anon");//解决静态资源文件 //<!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了; //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问--> filterChainDefinitionMap.put("/**", "authc"); // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 shiroFilterFactoryBean.setLoginUrl("/login"); // 登录成功后要跳转的链接 shiroFilterFactoryBean.setSuccessUrl("/index"); //未授权界面; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public DefaultWebSecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //设置realm. securityManager.setRealm(myShiroRealm()); //注入缓存管理器; securityManager.setCacheManager(ehCacheManager());//这个如果执行多次,也是同样的一个对象; //注入记住我管理器; securityManager.setRememberMeManager(rememberMeManager()); return securityManager; } /** * 身份认证realm; * (这个需要自己写,账号密码校验;权限等) * @return */ @Bean public MyShiroRealm myShiroRealm(){ MyShiroRealm myShiroRealm = new MyShiroRealm(); myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());; return myShiroRealm; } /** * 凭证匹配器 * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 * 所以我们需要修改下doGetAuthenticationInfo中的代码; * ) * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher(){// HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); HashedCredentialsMatcher hashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher(ehCacheManager()); hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法; hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5("")); return hashedCredentialsMatcher; } /** * 开启shiro aop注解支持. * 使用代理方式;所以需要开启代码支持; * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){ AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * shiro缓存管理器; * 需要注入对应的其它的实体类中: * 1、安全管理器:securityManager * 可见securityManager是整个shiro的核心; * @return */ @Bean public EhCacheManager ehCacheManager(){ System.out.println("ShiroConfiguration.getEhCacheManager()"); EhCacheManager cacheManager = new EhCacheManager(); cacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml"); return cacheManager; } /** * cookie对象; * @return */ @Bean public SimpleCookie rememberMeCookie(){ System.out.println("ShiroConfiguration.rememberMeCookie()"); //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe SimpleCookie simpleCookie = new SimpleCookie("rememberMe"); //<!-- 记住我cookie生效时间30天 ,单位秒;--> simpleCookie.setMaxAge(259200); return simpleCookie; } /** * cookie管理对象; * @return */ @Bean public CookieRememberMeManager rememberMeManager(){ System.out.println("ShiroConfiguration.rememberMeManager()"); CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(rememberMeCookie()); return cookieRememberMeManager; }}
常用的权限Filter
anon:所有url都都可以匿名访问;
authc: 需要认证才能进行访问;
user:配置记住我或认证通过可以访问;
MyShiroRealm.java
public class MyShiroRealm extends AuthorizingRealm { @Autowired private IUserInfoService iUserInfoService; @Autowired private ISysRoleService sysRoleService; /** * 认证信息.(身份验证) * : * Authentication 是用来验证用户身份 * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("MyShiroRealm.doGetAuthenticationInfo()"); //获取用户的输入的账号. String username = (String)token.getPrincipal(); System.out.println(token.getCredentials()); //通过username从数据库中查找 User对象,如果找到,没找到. //实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法 UserInfo userInfo= iUserInfoService.selectUserByUserNameWithRole(username); System.out.println("----->>userInfo="+userInfo); if(userInfo == null){ return null; } //账号判断; //加密方式; //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现// SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(// userInfo, //用户名// userInfo.getPassword(), //密码// ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//salt=username+salt// getName() //realm name// ); //明文: 若存在,将此用户存放到登录认证info中,无需自己做密码对比,Shiro会为我们进行密码对比校验 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( userInfo, //用户名 userInfo.getPassword(), //密码 getName() //realm name ); return authenticationInfo; } /** * 此方法调用 hasRole,hasPermission的时候才会进行回调. * * 权限信息.(授权): * 1、如果用户正常退出,缓存自动清空; * 2、如果用户非正常退出,缓存自动清空; * 3、如果我们修改了用户的权限,而用户不退出系统,修改的权限无法立即生效。 * (需要手动编程进行实现;放在service进行调用) * 在权限修改后调用realm中的方法,realm已经由spring管理,所以从spring中获取realm实例, * 调用clearCached方法; * :Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。 * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { /* * 当没有使用缓存的时候,不断刷新页面的话,这个代码会不断执行, * 当其实没有必要每次都重新设置权限信息,所以我们需要放到缓存中进行管理; * 当放到缓存中时,这样的话,doGetAuthorizationInfo就只会执行一次了, * 缓存过期之后会再次执行。 */ System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()"); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); UserInfo userInfo = (UserInfo)principals.getPrimaryPrincipal(); //实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法// UserInfo userInfo = userInfoService.findByUsername(username) //权限单个添加; // 或者按下面这样添加 //添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色// authorizationInfo.addRole("admin"); //添加权限// authorizationInfo.addStringPermission("userInfo:query"); ///在认证成功之后返回. //设置角色信息. //支持 Set集合 for(SysRole role:userInfo.getRoleList()){ authorizationInfo.addRole(role.getRole()); SysRole sysRole = sysRoleService.selectRoleByIdWithPermission(role.getId());//获取角色 for(SysPermission p:sysRole.getPermissions()){ authorizationInfo.addStringPermission(p.getPermission()); } } //设置权限信息.// authorizationInfo.setStringPermissions(getStringPermissions(userInfo.getRoleList())); return authorizationInfo; }}
在认证、授权内部实现机制中都有提到,最终处理都将交给Realm进行处理。因为在Shiro中,是通过Realm来获取应用程序中的用户、角色及权限信息的
认证实现
Shiro的认证过程最终会交由Realm执行,这时会调用Realm的getAuthenticationInfo(token)方法。
该方法主要执行以下操作:
1、检查提交的进行认证的令牌信息
2、根据令牌信息从数据源(通常为数据库)中获取用户信息
3、对用户信息进行匹配验证。
4、验证通过将返回一个封装了用户信息的AuthenticationInfo实例。
5、验证失败则抛出AuthenticationException异常信息。
而在我们的应用程序中要做的就是自定义一个Realm类,继承AuthorizingRealm抽象类,重载doGetAuthenticationInfo (),重写获取用户信息的方法。
- Spring Boot +Shiro 用户角色权限设计
- 用户角色权限系统完整设计(基于shiro)
- spring boot 集成shiro(用户授权和权限控制)
- 用户角色权限设计
- 用户角色权限设计
- 用户角色权限设计
- 用户角色权限设计
- 用户,角色,权限设计
- Spring Boot Shiro 权限管理
- Spring Boot Shiro 权限管理
- Spring Boot Shiro权限管理
- Spring Boot Shiro 权限管理
- Spring Boot Shiro权限管理
- Spring Boot Shiro 权限管理
- Spring Boot Shiro 权限管理
- Spring Boot Shiro 权限管理
- spring boot shiro 权限管理
- spring boot shiro权限管理
- asm,aop的利器
- DS18B20-51代码
- hive创建压缩分区表
- Android_ImageLoader
- (二) Ubuntu安装新版tensorflow以及目标检测API
- Spring Boot +Shiro 用户角色权限设计
- loslib.c解析(5.3.4)
- 简要谈谈javascript bind 方法
- 使用git上传本地文件到Gayhub(面向初学者)
- vue的进阶之路(一) 环境的配置
- Spring属性注入
- 文章标题
- C语言:自定义字符串函数
- 触摸[5] Scroller