shiro框架理解
1、securityManager,sessionManager,subject,Realm,拦截器。
2、DefaultSessionManager,JavaSe和JavaEE默认,DefaultWebSessionManager,web环境自己维护会话,ServletContainerSessionManager 默认的httpsession。
3、会话管理可以做到分布式会话,存入redis中,用到多服务之间
补充springboot2.x集成shiro代码
1、加依赖
<!--shiro和spring整合--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency> <!--shiro核心包--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.3.2</version> </dependency> <!--shiro与redis整合--> <dependency> <groupId>org.crazycake</groupId> <artifactId>shiro-redis</artifactId> <version>3.0.0</version> </dependency>
2、写配置,配置中必须要realm,securityManager,ShiroFilterFactoryBean用来生成Filter,shiro用Redis来存储存储session的,所以需要redis等一系列的bean配置,以及sessionManager
package com.ihr.system; import com.ihr.common.shiro.realm.IhrmRealm; import com.ihr.common.shiro.session.CustomSessionManager; import com.ihr.system.shiro.realm.UserRealm; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.crazycake.shiro.RedisCacheManager; import org.crazycake.shiro.RedisManager; import org.crazycake.shiro.RedisSessionDAO; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; @Configuration public class ShiroConfiguration { //1.创建realm @Bean public IhrmRealm getRealm() { return new UserRealm(); } //2.创建安全管理器 @Bean public SecurityManager getSecurityManager(IhrmRealm realm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm); //将自定义的会话管理器注册到安全管理器中 securityManager.setSessionManager(sessionManager()); //将自定义的redis缓存管理器注册到安全管理器中 securityManager.setCacheManager(cacheManager()); return securityManager; } //3.配置shiro的过滤器工厂 /** * 再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制 * */ @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { //1.创建过滤器工厂 ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean(); //2.设置安全管理器 filterFactory.setSecurityManager(securityManager); //3.通用配置(跳转登录页面,未授权跳转的页面) filterFactory.setLoginUrl("/autherror?code=1");//跳转url地址 filterFactory.setUnauthorizedUrl("/autherror?code=2");//未授权的url //4.设置过滤器集合 Map<String,String> filterMap = new LinkedHashMap<>(); //anon -- 匿名访问 filterMap.put("/sys/login","anon"); filterMap.put("/autherror","anon"); //注册 //authc -- 认证之后访问(登录) filterMap.put("/**","authc"); //perms -- 具有某中权限 (使用注解配置授权) filterFactory.setFilterChainDefinitionMap(filterMap); return filterFactory; } @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private int port; /** * 1.redis的控制器,操作redis */ public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setHost(host); redisManager.setPort(port); return redisManager; } /** * 2.sessionDao */ public RedisSessionDAO redisSessionDAO() { RedisSessionDAO sessionDAO = new RedisSessionDAO(); sessionDAO.setRedisManager(redisManager()); return sessionDAO; } /** * 3.会话管理器 */ public DefaultWebSessionManager sessionManager() { CustomSessionManager sessionManager = new CustomSessionManager(); sessionManager.setSessionDAO(redisSessionDAO()); //禁用cookie sessionManager.setSessionIdCookieEnabled(false); //禁用url重写 url;jsessionid=id sessionManager.setSessionIdUrlRewritingEnabled(false); return sessionManager; } /** * 4.缓存管理器 */ public RedisCacheManager cacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(redisManager()); return redisCacheManager; } //开启对shior注解的支持 @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; } }
realm代码
package com.ihr.system.shiro.realm; import com.ihr.common.shiro.realm.IhrmRealm; import com.ihr.domain.system.Permission; import com.ihr.domain.system.User; import com.ihr.domain.system.response.ProfileResult; import com.ihr.system.service.PermissionService; import com.ihr.system.service.UserService; import org.apache.shiro.authc.*; import org.springframework.beans.factory.annotation.Autowired; import java.util.HashMap; import java.util.List; import java.util.Map; public class UserRealm extends IhrmRealm { @Autowired private UserService userService; @Autowired private PermissionService permissionService; //认证方法 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //1.获取用户的手机号和密码 UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken; String mobile = upToken.getUsername(); String password = new String( upToken.getPassword()); //2.根据手机号查询用户 User user = userService.findByMobile(mobile); //3.判断用户是否存在,用户密码是否和输入密码一致 if(user != null && user.getPassword().equals(password)) { //4.构造安全数据并返回(安全数据:用户基本数据,权限信息 profileResult) ProfileResult result = null; if("user".equals(user.getLevel())) { result = new ProfileResult(user); }else { Map map = new HashMap(); if("coAdmin".equals(user.getLevel())) { map.put("enVisible","1"); } List<Permission> list = permissionService.findAll(map); result = new ProfileResult(user,list); } //构造方法:安全数据,密码,realm域名 SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(result,user.getPassword(),this.getName()); return info; } //返回null,会抛出异常,标识用户名和密码不匹配 return null; } }
package com.ihr.common.shiro.realm; import com.ihr.domain.system.response.ProfileResult; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import java.util.Set; /** * @author: yangchun * @description: * @date: Created in 2020-06-27 9:27 */ public class IhrmRealm extends AuthorizingRealm { public void setName(String name) { super.setName("ihrmRealm"); } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //1.获取安全数据 ProfileResult result = (ProfileResult)principalCollection.getPrimaryPrincipal(); //2.获取权限信息 Set<String> apisPerms = (Set<String>)result.getRoles().get("apis"); //3.构造权限数据,返回值 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.setStringPermissions(apisPerms); return info; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { return null; } }
package com.ihr.common.shiro.session; import org.apache.shiro.web.servlet.ShiroHttpServletRequest; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.apache.shiro.web.util.WebUtils; import org.springframework.util.StringUtils; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.Serializable; public class CustomSessionManager extends DefaultWebSessionManager { /** * 头信息中具有sessionid * 请求头:Authorization: sessionid * * 指定sessionId的获取方式 */ protected Serializable getSessionId(ServletRequest request, ServletResponse response) { //获取请求头Authorization中的数据 String id = WebUtils.toHttp(request).getHeader("Authorization"); if(StringUtils.isEmpty(id)) { //如果没有携带,生成新的sessionId return super.getSessionId(request,response); }else{ //请求头信息:bearer sessionid id = id.replaceAll("Bearer ",""); //返回sessionId; request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header"); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); return id; } } }
realm里面的主要负责两件事情,第一件就是认证,第二件事情进行授权,然后将用户授权数据存入redis,后面鉴权时从redis里面根据session_id进行取出来。通过realm来区分一个个的域,所以统一认证域里面需要统一名称,名称通过setName来设置
配置文件如下
#服务配置 server: port: 9002 #spring配置 spring: #1.应用配置 application: name: ihrm-system #指定服务名 #2.数据库连接池 datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/ihrm?useUnicode=true&characterEncoding=utf8 username: root password: 111111 #3.JPA jpa: database: MySQL show-sql: true open-in-view: true redis: host: 127.0.0.1 port: 6379 jwt: config: key: saas-ihrm ttl: 3600000
负责鉴权部分代码如下,SQL也如下
/** * 根据id删除 */ /** * 根据id删除 */ @RequiresPermissions(value = "API-USER-DELETE") @RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE) public Result delete(@PathVariable(value = "id") String id) { userService.deleteById(id); return new Result(ReturnCode.SUCCESS); }
/* Navicat MySQL Data Transfer Source Server : localhost Source Server Version : 50558 Source Host : localhost:3306 Source Database : shiro_db Target Server Type : MYSQL Target Server Version : 50558 File Encoding : 65001 Date: 2018-11-29 18:03:45 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for pe_permission -- ---------------------------- DROP TABLE IF EXISTS `pe_permission`; CREATE TABLE `pe_permission` ( `id` varchar(40) NOT NULL COMMENT '主键', `name` varchar(255) DEFAULT NULL COMMENT '权限名称', `code` varchar(20) DEFAULT NULL, `description` text COMMENT '权限描述', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of pe_permission -- ---------------------------- INSERT INTO `pe_permission` VALUES ('1', '添加用户', 'user-add', null); INSERT INTO `pe_permission` VALUES ('2', '查询用户', 'user-find', null); INSERT INTO `pe_permission` VALUES ('3', '更新用户', 'user-update', null); INSERT INTO `pe_permission` VALUES ('4', '删除用户', 'user-delete', null); -- ---------------------------- -- Table structure for pe_role -- ---------------------------- DROP TABLE IF EXISTS `pe_role`; CREATE TABLE `pe_role` ( `id` varchar(40) NOT NULL COMMENT '主键ID', `name` varchar(40) DEFAULT NULL COMMENT '权限名称', `description` varchar(255) DEFAULT NULL COMMENT '说明', PRIMARY KEY (`id`), UNIQUE KEY `UK_k3beff7qglfn58qsf2yvbg41i` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of pe_role -- ---------------------------- INSERT INTO `pe_role` VALUES ('1', '系统管理员', '系统日常维护'); INSERT INTO `pe_role` VALUES ('2', '普通员工', '普通操作权限'); -- ---------------------------- -- Table structure for pe_role_permission -- ---------------------------- DROP TABLE IF EXISTS `pe_role_permission`; CREATE TABLE `pe_role_permission` ( `role_id` varchar(40) NOT NULL COMMENT '角色ID', `permission_id` varchar(40) NOT NULL COMMENT '权限ID', PRIMARY KEY (`role_id`,`permission_id`), KEY `FK74qx7rkbtq2wqms78gljv87a0` (`permission_id`), KEY `FKee9dk0vg99shvsytflym6egxd` (`role_id`), CONSTRAINT `fk-p-rid` FOREIGN KEY (`role_id`) REFERENCES `pe_role` (`id`), CONSTRAINT `fk-pid` FOREIGN KEY (`permission_id`) REFERENCES `pe_permission` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of pe_role_permission -- ---------------------------- INSERT INTO `pe_role_permission` VALUES ('1', '1'); INSERT INTO `pe_role_permission` VALUES ('1', '2'); INSERT INTO `pe_role_permission` VALUES ('2', '2'); INSERT INTO `pe_role_permission` VALUES ('1', '3'); INSERT INTO `pe_role_permission` VALUES ('1', '4'); -- ---------------------------- -- Table structure for pe_user -- ---------------------------- DROP TABLE IF EXISTS `pe_user`; CREATE TABLE `pe_user` ( `id` varchar(40) NOT NULL COMMENT 'ID', `username` varchar(255) NOT NULL COMMENT '用户名称', `password` varchar(255) DEFAULT NULL COMMENT '密码', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of pe_user -- ---------------------------- INSERT INTO `pe_user` VALUES ('1', 'zhangsan', '123456'); INSERT INTO `pe_user` VALUES ('2', 'lisi', '123456'); INSERT INTO `pe_user` VALUES ('3', 'wangwu', '123456'); -- ---------------------------- -- Table structure for pe_user_role -- ---------------------------- DROP TABLE IF EXISTS `pe_user_role`; CREATE TABLE `pe_user_role` ( `role_id` varchar(40) NOT NULL COMMENT '角色ID', `user_id` varchar(40) NOT NULL COMMENT '权限ID', KEY `FK74qx7rkbtq2wqms78gljv87a1` (`role_id`), KEY `FKee9dk0vg99shvsytflym6egx1` (`user_id`), CONSTRAINT `fk-rid` FOREIGN KEY (`role_id`) REFERENCES `pe_role` (`id`), CONSTRAINT `fk-uid` FOREIGN KEY (`user_id`) REFERENCES `pe_user` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of pe_user_role -- ---------------------------- INSERT INTO `pe_user_role` VALUES ('1', '1');
项目代码地址github,其中包含springsecurity实现基于jwt的rbac鉴权
https://github.com/fengjiangxiaofeiyang/shiroAndSecurity