上一章已经完整的搭建好了一个框架,现在需要集成shiro来达到前后端分离之后,对用户的权限控制
第一步,建立管理的一系列表sql如下
/*
Navicat Premium Data Transfer
Source Server : diss
Source Server Type : MySQL
Source Server Version : 50725
Source Host : rm-bp14to8mn841swfk6so.mysql.rds.aliyuncs.com:3306
Source Schema : wy
Target Server Type : MySQL
Target Server Version : 50725
File Encoding : 65001
Date: 12/10/2019 10:46:41
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for sys_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission` (
`code` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '权限编码,主键',
`flag` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限标识',
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限名称',
`order_by` int(11) NULL DEFAULT NULL COMMENT '排序',
`parent_code` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '上级权限',
`type` int(11) NULL DEFAULT NULL COMMENT '类型,0菜单,1按钮',
`url` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '访问路径',
PRIMARY KEY (`code`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_permission
-- ----------------------------
INSERT INTO `sys_permission` VALUES ('0001', 'admin:user:list', '用户列表', 1, NULL, NULL, NULL);
INSERT INTO `sys_permission` VALUES ('0002', 'admin:user:info', '用户详情', 1, NULL, NULL, NULL);
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色名',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `id`(`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES (1, '总管理');
INSERT INTO `sys_role` VALUES (2, '审核管理');
INSERT INTO `sys_role` VALUES (3, '财务管理');
INSERT INTO `sys_role` VALUES (4, '商城管理');
INSERT INTO `sys_role` VALUES (5, '客服');
-- ----------------------------
-- Table structure for sys_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_permission`;
CREATE TABLE `sys_role_permission` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`permission_code` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限',
`role_id` int(11) NULL DEFAULT NULL COMMENT '角色',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `id`(`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_role_permission
-- ----------------------------
INSERT INTO `sys_role_permission` VALUES (1, '0001', 1);
INSERT INTO `sys_role_permission` VALUES (2, '0001', 1);
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`username` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '管理用户',
`address` varchar(0) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`create_time` datetime(0) NULL DEFAULT NULL,
`email` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`phone` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`sex` int(11) NULL DEFAULT NULL,
`real_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '真实姓名',
PRIMARY KEY (`username`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('admin', NULL, '2019-04-19 11:25:03', NULL, '123456', NULL, NULL, NULL);
-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_id` int(11) NULL DEFAULT NULL COMMENT '角色',
`username` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '系统用户',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `id`(`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES (1, 1, 'admin');
SET FOREIGN_KEY_CHECKS = 1;
其中,权限控制共五张表,sys_user是系统用户表,sys_role是角色表,sys_user_role是用户角色表(用来表示这个用户有哪几种角色),sys_permission是系统权限表,sys_role_permission是角色权限表(这个角色有哪几种权限)
关于上面五张表的关系,可以用以下例子来解释,如果还是不懂可以百度一下,系统角色与权限之间的关系
某公司一个系统中,有增删改查四种权限,公司董事长可以操作所有权限,公司总经理可以操作增改查三种权限,业务员可以操作查询权限
其中增删改查四种权限,在sys_permission表中表现为四条数据
sys_role就有三条数据,董事长、总经理、业务员
那么sys_role_permission表中董事长关联了四条数据,总经理关联了三条数据,业务员关联了一条数据,表示各自所具有的功能权限
现在A用户就任公司董事长,B用户就任总经理,C用户就任业务员
sys_user中设置一个管理用户为 A ,B用户和C用户
此时 在sys_user_role中将A用户关联董事长,那么A用户就可以操作增删改查四条功能,B用户关联总经理那么B用户就可以操作三条功能,C用户同理
如果此时B用户抓住了A用户的小辫子,把他拉下台了,两人的位置互换,B用户关联董事长,A用户关联总经理,那么B用户就可以操作四条数据,A用户只能操作三条数据
这么做的好处在与可以将权限的细粒度划分的极为细小,并且操作方便。
上面讲述了权限表之间的关系,回到正题,将以上五张表,使用generator导入到项目中

此时我们进行第二步
配置shiro的相关maven依赖
<!-- 继承shiro关联包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
以上依赖全部配置完成之后,就该写配置文件了
其中配置文件分为ShiroConfig、MyRealm、还有一个CustomSessionManager用来自定义配置session
ShiroConfig
package com.fzf.web.config;
import com.fzf.web.common.CustomSessionManager;
import com.fzf.web.exception.MyExceptionHandler;
import com.fzf.web.filter.CORSAuthenticationFilter;
import com.fzf.web.realm.MyRealm;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.web.servlet.HandlerExceptionResolver;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public Realm realm() {
return new MyRealm();
}
@Bean
public CacheManager cacheManager() {
return new MemoryConstrainedCacheManager();
}
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
//SecurityUtils.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
//配置不会被拦截的链接,顺序判断
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/sys/user/login", "anon");
//authc:所有url必须通过认证才能访问,anon:所有url都可以匿名访问
filterChainDefinitionMap.put("/**", "authc");
// filterChainDefinitionMap.put("/**", "corsAuthenticationFilter");
// shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
shiroFilter.setLoginUrl("/unauth");
//自定义过滤器
Map<String, Filter> filterMap = new LinkedHashMap<>();
filterMap.put("corsAuthenticationFilter", corsAuthenticationFilter());
shiroFilter.setFilters(filterMap);
return shiroFilter;
}
/**
* cookie对象;
* rememberMeCookie()方法是设置Cookie的生成模版,比如cookie的name,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管理对象;
* rememberMeManager()方法是生成rememberMe管理器,而且要将这个rememberMe管理器设置到securityManager中
* @return
*/
@Bean
public CookieRememberMeManager rememberMeManager(){
//System.out.println("ShiroConfiguration.rememberMeManager()");
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
return cookieRememberMeManager;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager sm = new DefaultWebSecurityManager();
sm.setRealm(realm());
sm.setCacheManager(cacheManager());
//注入记住我管理器
sm.setRememberMeManager(rememberMeManager());
//注入自定义sessionManager
sm.setSessionManager(sessionManager());
return sm;
}
//自定义sessionManager
@Bean
public SessionManager sessionManager() {
return new CustomSessionManager();
}
public CORSAuthenticationFilter corsAuthenticationFilter(){
return new CORSAuthenticationFilter();
}
/**
* Shiro生命周期处理器 * @return
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能 * @return
*/
@Bean
@DependsOn({"lifecycleBeanPostProcessor"})
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 注册全局异常处理
* @return
*/
@Bean(name = "exceptionHandler")
public HandlerExceptionResolver handlerExceptionResolver() {
return new MyExceptionHandler();
}
}
CustomSessionManager
package com.fzf.web.common;
import com.alibaba.druid.util.StringUtils;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;
public class CustomSessionManager extends DefaultWebSessionManager {
private static final Logger logger = LoggerFactory.getLogger(CustomSessionManager.class);
private static final String AUTHORIZATION = "Authorization";
private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
public CustomSessionManager() {
super();
setGlobalSessionTimeout(DEFAULT_GLOBAL_SESSION_TIMEOUT * 48);
}
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
String sessionId = WebUtils.toHttp(request).getHeader(AUTHORIZATION);//如果请求头中有 Authorization 则其值为sessionId
if (!StringUtils.isEmpty(sessionId)) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return sessionId;
} else {
//否则按默认规则从cookie取sessionId
return super.getSessionId(request, response);
}
}
}
MyRealm
package com.fzf.web.realm;
import com.fzf.web.mapper.SysPermissionMapper;
import com.fzf.web.mapper.SysRoleMapper;
import com.fzf.web.mapper.SysUserMapper;
import com.fzf.web.model.SysPermission;
import com.fzf.web.model.SysRole;
import com.fzf.web.model.SysUser;
import com.fzf.web.util.ValidateUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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 javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MyRealm extends AuthorizingRealm {
@Resource
private SysUserMapper sysUserMapper;
@Resource
private SysRoleMapper sysRoleMapper;
@Resource
private SysPermissionMapper sysPermissionMapper;
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = principalCollection.getPrimaryPrincipal().toString();
//根据用户名查询出用户记录
SysUser sysUser = sysUserMapper.selectByPrimaryKey(username);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
List<SysRole> roleList = sysRoleMapper.selectRolesByUsername(sysUser.getUsername());
Set<String> roles=new HashSet<String>();
if(roleList.size()>0){
for(SysRole role:roleList){
roles.add(role.getName());
//根据角色id查询所有资源
List<SysPermission> permissionList=sysPermissionMapper.selectByRoleId(role.getId());
for(SysPermission permission:permissionList){
if (ValidateUtils.isNotEmpty(permission.getFlag())){
info.addStringPermission(permission.getFlag()); // 添加权限
}
}
}
}
info.setRoles(roles);
return info;
}
/**
* 权限认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = authenticationToken.getPrincipal().toString();
SysUser sysUser = sysUserMapper.selectByPrimaryKey(username);
if(sysUser!=null){
AuthenticationInfo authcInfo=new SimpleAuthenticationInfo(sysUser.getUsername(),sysUser.getPassword(),"xxx");
return authcInfo;
}else{
return null;
}
}
}
其中MyRealm的doGetAuthorizationInfo方法可根据自己权限架构来自己写,因为有的项目不需要这么详细的操作,只要三张表就可以使用,具体可以根据自己的权限框架来写(多走几遍debug就能看懂doGetAuthorizationInfo知道怎么改了)
浙公网安备 33010602011771号