JAVA项目实战-shiro框架使用(一)
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
**/
@Configuration
public 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;
}
}
依赖maven支持
<!-- SpringBoot AOP包,用于Shiro授权 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!--Shiro核心包 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.0</version> </dependency> <!--fastJson--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.2</version> <scope>provided</scope> </dependency>