shiro主要处理项目中用户模块、角色模块、菜单模块、权限模块功能,非常实用。
学习点一:
构建数据库表结构(通用)
1.用户表 (可根据角色,添加相应字段)
-- ---------------------------- -- Table structure for sys_user -- ---------------------------- DROP TABLE IF EXISTS `sys_user`; CREATE TABLE `sys_user` ( `user_id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '姓名', `account` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '登录账户(8位字母)', `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码', `status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '状态(0-正常;1-不可用)', `create_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间', `create_id` bigint(11) NULL DEFAULT NULL COMMENT '创建人id', `update_time` timestamp(6) NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '修改时间', `update_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人id', PRIMARY KEY (`user_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 41 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
2.角色表
-- ---------------------------- -- Table structure for sys_role -- ---------------------------- DROP TABLE IF EXISTS `sys_role`; CREATE TABLE `sys_role` ( `role_id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色名称', `remarks` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注', `status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '状态(0-正常;1-不可用)', `create_time` timestamp(6) NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间', `create_id` bigint(11) NULL DEFAULT NULL COMMENT '创建人id', `update_time` timestamp(6) NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '修改时间', `update_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人id', PRIMARY KEY (`role_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 24 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
3.角色用户表
-- ---------------------------- -- Table structure for sys_user_role -- ---------------------------- DROP TABLE IF EXISTS `sys_user_role`; CREATE TABLE `sys_user_role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_id` bigint(20) NOT NULL COMMENT '用户ID', `role_id` bigint(20) NOT NULL COMMENT '角色ID', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `user_id_unique`(`user_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 182 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
4.菜单表
-- ---------------------------- -- Table structure for sys_menu -- ---------------------------- DROP TABLE IF EXISTS `sys_menu`; CREATE TABLE `sys_menu` ( `menu_id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '菜单名称', `icon` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '菜单对应的图标', `parent_id` bigint(20) NULL DEFAULT NULL COMMENT '父菜单id', `status` tinyint(255) NOT NULL DEFAULT 0 COMMENT '0-可用;1-不可用', `identity` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '路由信息', PRIMARY KEY (`menu_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 46 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
5.权限表(shiro框架特有)
-- ---------------------------- -- Table structure for sys_permission -- ---------------------------- DROP TABLE IF EXISTS `sys_permission`; CREATE TABLE `sys_permission` ( `permission_id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '权限名称', `sn` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '权限对应URL编号 例(order:pay)', `resources` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限对应的url(shiro框架用,例 order/pay)', `parent_id` bigint(20) NULL DEFAULT NULL COMMENT '父id', `menu_id` bigint(20) NULL DEFAULT NULL COMMENT '菜单id', `status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '状态(0-可用;1-不可用)', PRIMARY KEY (`permission_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 96 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
6.角色权限表(shiro框架特有)
-- ---------------------------- -- Table structure for sys_role_permission -- ---------------------------- DROP TABLE IF EXISTS `sys_role_permission`; CREATE TABLE `sys_role_permission` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `role_id` bigint(20) NULL DEFAULT NULL COMMENT '角色id', `permission_id` bigint(20) NULL DEFAULT NULL COMMENT '资源id', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1177506881387504299 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色资源信息表' ROW_FORMAT = Dynamic;
学习点二
数据库操作,主要采用mybatis-plus
permissionMapper.xml(注:1.需要修改文件中实体和mapper位置)
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mapper namespace="com.sf.vsolution.hb.sfce.mapper.PermissionMapper" > <resultMap id="BaseResultMap" type="com.sf.vsolution.hb.sfce.pojo.entity.Permission" > <id column="permission_id" property="permissionId" jdbcType="BIGINT" /> <result column="name" property="name" jdbcType="VARCHAR" /> <result column="sn" property="sn" jdbcType="VARCHAR" /> <result column="resources" property="resources" jdbcType="VARCHAR" /> <result column="parent_id" property="parentId" jdbcType="BIGINT" /> <result column="menu_id" property="menuId" jdbcType="BIGINT" /> <result column="status" property="status" jdbcType="TINYINT" /> </resultMap> <!-- 根据用多个角色id查询拥有的权限--> <select id="getPermissionsByRoles" resultMap="BaseResultMap" parameterType="java.util.List" > SELECT DISTINCT * FROM( SELECT p.* FROM sys_permission p LEFT JOIN sys_role_permission rp ON p.permission_id = rp.permission_id WHERE rp.role_id in <foreach collection="roleIds" index="index" item="id" open="(" separator="," close=")"> #{id} </foreach> AND p.`status` = 0 AND p.sn IS NOT NULL ORDER BY p.permission_id ) a </select> <!-- 查询系统所有有效权限--> <select id="getAllPermissions" resultMap="BaseResultMap" > select permission_id, `name`, sn, resources, parent_id, menu_id, `status` from sys_permission WHERE `status` = 0 AND sn IS NOT NULL AND resources IS NOT NULL </select> </mapper>
permissionMapper
package com.sf.vsolution.hb.sfce.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.sf.vsolution.hb.sfce.pojo.entity.Permission; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import java.util.List; /** * @description: 权限接口 * @author: zhucj * @date: 2019-11-13 9:33 */ @Repository public interface PermissionMapper extends BaseMapper<Permission> { /** * 根据用多个角色id查询拥有的权限 * @param roleIds * @return List */ List<Permission> getPermissionsByRoles(@Param("roleIds") List<Long> roleIds); /** * 查询系统所有有效权限 * @return List */ List<Permission> getAllPermissions(); }
permissionService
package com.sf.vsolution.hb.sfce.service; import com.baomidou.mybatisplus.extension.service.IService; import com.sf.vsolution.hb.sfce.pojo.entity.Permission; import com.sf.vsolution.hb.sfce.pojo.entity.Role; import java.util.List; /** * @Description shiro框架权限控制 * @Author zhucj * @Date 2019/5/6 17:13 */ public interface PermissionService { /** * 根据用户的多个角色查询拥有的权限 * @param roles * @return List */ List<Permission> findPermissionsByRoles(List<Role> roles); /** * 查询系统所有有效权限 * @return List */ List<Permission> loadAllPermissions(); }
PermissionServiceImpl
package com.sf.vsolution.hb.sfce.service.impl; import com.sf.vsolution.hb.sfce.mapper.PermissionMapper; import com.sf.vsolution.hb.sfce.pojo.entity.Permission; import com.sf.vsolution.hb.sfce.pojo.entity.Role; import com.sf.vsolution.hb.sfce.service.PermissionService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; /** * @ClassName PermissionServiceImpl * @Description PermissionServiceImpl * @Author YangLei * @Date 2019/5/7 10:47 * @Version 1.0 **/ @Service public class PermissionServiceImpl implements PermissionService { private static final Logger log = LoggerFactory.getLogger(PermissionServiceImpl.class); @Autowired private PermissionMapper permissionMapper; @Override public List<Permission> findPermissionsByRoles(List<Role> roles){ List<Long> roleIds = new ArrayList<>(); for (Role role : roles) { if(role != null){ roleIds.add(role.getRoleId()); } } if(roleIds.size() <= 0){ return null; } try{ return permissionMapper.getPermissionsByRoles(roleIds); }catch (Exception e){ log.error("根据用户角色查询拥有权限时异常:{}" , e); return null; } } @Override public List<Permission> loadAllPermissions() { return permissionMapper.getAllPermissions(); } }
学习点三
ShiroRealm 权限的校验,每次请求接口是,获取当前角色信息权限,交给shiro框架
package com.sf.detectcore.config.shiro; import com.sf.detectcore.config.constant.SystemConstants; import com.sf.detectcore.entity.SysPermission; import com.sf.detectcore.entity.SysRole; import com.sf.detectcore.service.SysPermissionService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; 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 org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils; import java.util.HashSet; import java.util.List; import java.util.Set; /** * @ClassName ShiroRealm * @Description shiro Realm * @Author huyong * @Date 2019/10/16 17:34 * @Version 1.0 */ public class ShiroRealm extends AuthorizingRealm { private static final Logger log = LoggerFactory.getLogger(ShiroRealm.class); @Autowired SysPermissionService sysPermissionService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { Subject subject = SecurityUtils.getSubject(); List<SysRole> roles = (List)subject.getSession().getAttribute(SystemConstants.SESSION_USER_ROLES); // 查询用户拥有的权限并交给shiro框架 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); List<SysPermission> permissions = sysPermissionService.findPermissionsByRoles(roles); Set<String> sns = new HashSet<>(); for (SysPermission permission : permissions) { if (null == permission || StringUtils.isEmpty(permission.getResources())) { continue; } sns.add(permission.getSn()); } //log.info("当前用户拥有的权限包括:{}" ,sns.toString()); info.addStringPermissions(sns); return info; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken upToken = (UsernamePasswordToken)token; String account = (String)upToken.getPrincipal(); char[] pwd = upToken.getPassword(); StringBuilder sb = new StringBuilder(); for (char c : pwd) { sb.append(c); } // 1.认证工号 Object principal = account; // 2.认证密码 Object credentials = sb.toString(); // 框架进行登录判断 return new SimpleAuthenticationInfo(principal, credentials, getName()); } }
ShiroConfig 权限的配置,配置特别接口和加载权限内容,权限拦截接口
package com.sf.vsolution.hb.sfce.util.shiro;import com.sf.vsolution.hb.sfce.pojo.entity.Permission;import com.sf.vsolution.hb.sfce.service.PermissionService;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.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.util.StringUtils;import org.springframework.web.filter.DelegatingFilterProxy;import java.util.*;/** * @ClassName ShiroConfig * @Description 权限框架相关配置 * @Author YangLei * @Date 2019/5/7 10:30 * @Version 1.0 **/@Configurationpublic class ShiroConfig { private static Logger log = LoggerFactory.getLogger(ShiroConfig.class); @Autowired PermissionService permissionService; /** * 注入ShiroRealm */ @Bean public ShiroRealm shiroRealm() { return new ShiroRealm(); } /** * 注入securityManager */ @Bean public SecurityManager securityManager() { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRealm(shiroRealm()); return manager; } @Bean("shiroFilter") public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { //1 定义shiroFactoryBean ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean(); //2 设置securityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //设置默认登录的url shiroFilterFactoryBean.setLoginUrl("/login/goLogin"); shiroFilterFactoryBean.setUnauthorizedUrl("/login/unauthorized"); //3 查询系统中所有权限并交给shiro框架 Map<String,String> result = new LinkedHashMap<>(); // authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问 /* ===============静态资源放行区================== */ result.put("/resources/**", "anon"); result.put("/druid/**", "anon"); result.put("/static/**", "anon"); result.put("/swagger/**", "anon"); result.put("/swagger-ui.html", "anon"); result.put("/swagger-resources/**", "anon"); result.put("/webjars/**", "anon"); result.put("/v2/api-docs", "anon"); result.put("/index.html", "anon"); result.put("/order.html", "anon"); result.put("/css/**", "anon"); result.put("/fonts/**", "anon"); result.put("/img/**", "anon"); result.put("/js/**", "anon"); result.put("/favicon.ico*", "anon"); /*===============项目测试接口放行区================*/ result.put("/test/order", "anon"); /*===============项目部分接口放行区================*/ // h5下单接口 result.put("/order/createOrder", "anon"); /*===============特定接口放行区================*/ //注销,退出登录 result.put("/login/logout", "logout"); //拦截到登录(返回json,前端控制跳转到登录页面) result.put("/login/goLogin", "anon"); //用户名密码登录 result.put("/login/login", "anon"); result.put("/login", "anon"); result.put("/login/logout", "anon"); //登录验证码相关 result.put("/login/getVerifyCodeImage" , "anon"); result.put("/login/getVerifyCodeTest" , "anon"); /*===============登录后需要有权限才能访问区================*/ List<Permission> permissions = permissionService.loadAllPermissions(); for (Permission permission : permissions) { if (permission.getResources()==null || permission.getSn() == null) { continue; } String resource = permission.getResources(); String perm = "perms[" + permission.getSn() +"]"; result.put(resource, perm); } /*===============拦截上述以外,所有接口区================*/ result.put("/**", "authc"); Set<Map.Entry<String, String>> set = result.entrySet(); Iterator<Map.Entry<String, String>> it = set.iterator(); //过滤为空的 Set<String> keys = result.keySet(); Map<String,String> map = new LinkedHashMap<>(); for(String key : keys){ if(!StringUtils.isEmpty(key) && !StringUtils.isEmpty(result.get(key))){ map.put(key, result.get(key)); } } shiroFilterFactoryBean.setFilterChainDefinitionMap(map); log.info("Project All permission:{}",result); return shiroFilterFactoryBean; } /** * 注册AuthorizationAttributeSourceAdvisor * 如果要开启注解@RequiresRoles等注解,必须添加 */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager manager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(manager); return advisor; } @Bean public FilterRegistrationBean delegatingFilterProxy(){ FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); DelegatingFilterProxy proxy = new DelegatingFilterProxy(); proxy.setTargetFilterLifecycle(true); proxy.setTargetBeanName("shiroFilter"); filterRegistrationBean.setFilter(proxy); return filterRegistrationBean; }}