首页
归档
留言
广告合作
友链
美女主播
Search
1
博瑞GE车机升级/降级
5,179 阅读
2
修改elementUI中el-table树形结构图标
4,549 阅读
3
Mac打印机设置黑白打印
4,542 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,379 阅读
5
intelliJ Idea 2022.2.X破解
4,099 阅读
Java
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Spring Cloud
Mac
mybatis
WordPress
Nacos
Spring Cloud Alibaba
Mybatis-Plus
jQuery
Java Script
asp.net
微信小程序
Sentinel
UniApp
MySQL
asp.net core
IntelliJ IDEA
Jpa
树莓派
Laughing
累计撰写
576
篇文章
累计收到
1,425
条评论
首页
栏目
Java
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
广告合作
友链
美女主播
搜索到
4
篇与
的结果
2020-10-14
超详细Vue+Spring Boot整合Shiro前后端分离架构-Shiro权限分配设计(四)
前端设计使用npm创建web工程通过以下命令创建一个web工程vue init webpack shiroui添加axios,用于请求后端服务cnpm install axios --save在工程main.js中引入axiosVue.config.productionTip = false axios.defaults.baseURL = 'localhost:8080' Vue.prototype.$axios = axios添加elementuicnpm i element-ui -S在工程main.js中引入elementuiimport ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' ··· Vue.use(ElementUI)main.js预览实现登录功能前端界面<template> <div id="app"> <el-form ref="form" :model="user" label-width="80px"> <el-form-item label="用户编号"> <el-input v-model="user.userCode" placeholder="请输入用户编号"></el-input> </el-form-item> <el-form-item label="密 码"> <el-input v-model="user.password" placeholder="请输入密码" show-password></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="onSubmit">登录</el-button> <el-button>取消</el-button> </el-form-item> </el-form> <el-breadcrumb separator-class="el-icon-arrow-right"> <el-breadcrumb-item :to="{ path: '/' }">用户管理</el-breadcrumb-item> <el-breadcrumb-item :to="{ path: '/role' }">角色管理</el-breadcrumb-item> <el-breadcrumb-item :to="{ path: '/permission' }">权限管理</el-breadcrumb-item> <el-breadcrumb-item>角色分配</el-breadcrumb-item> <el-breadcrumb-item>权限分配</el-breadcrumb-item> </el-breadcrumb> <router-view/> </div> </template> <script> export default { name: 'App', data () { return { user: { userCode: undefined, password: undefined } } }, methods: { onSubmit: function () { debugger this.$axios({ method: 'post', url: '/shiro/login', params: this.user }).then(result => { this.$message(result.data) }).catch(e => { console.log(e) }) } } } </script> <style> </style>登录后端代码/** * @author laughing * @date 2020/10/11 * @site https://www.lisen.org */ @RestController @RequestMapping("/shiro") public class ShiroController { private final Logger logger = LoggerFactory.getLogger(ShiroController.class); @Resource ShiroService shiroService; @RequestMapping("/findUserByUserCode") public ShiroUser findUserByUserCode() { String userCode = "lisen"; ShiroUser shiroUser = shiroService.findUserByCode(userCode); return shiroUser; } @RequestMapping("/login") public String login(@RequestParam("userCode") String userCode, @RequestParam("password") String password) { // password = new SimpleHash(AuthenConst.HASH_ALGORITHM_NAME,password,AuthenConst.SALT,AuthenConst.HASH_INTERACTIONS).toHex(); UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userCode, password); Subject subject = SecurityUtils.getSubject(); try { //进行验证,这里可以捕获异常,然后返回对应信息 subject.login(usernamePasswordToken); } catch (UnknownAccountException e) { logger.error("用户名不存在!", e); return "用户名不存在!"; } catch (AuthenticationException e) { logger.error("账号或密码错误!", e); return "账号或密码错误!"; } catch (AuthorizationException e) { logger.error("没有权限!", e); return "没有权限"; } return "login success"; } @RequestMapping("/index") public String index(String userCode, String password) { return "index"; } @RequiresPermissions("permission") @RequestMapping("/permission") public String permission() { return "permission"; } @RequiresPermissions("dept:add") @RequestMapping("/deptadd") public String deptAdd() { return "dept:add"; } @RequestMapping("/nopermission") public String noPermission() { return "noPermission"; } }前端权限调用<template> <div> <el-form> <el-form-item> <el-button @click="findAll">加载用户信息</el-button> <el-button @click="dialogFormVisible=true">增加用户</el-button> <el-button @click="dialogFormRoleVisible=true">分配岗位</el-button> </el-form-item> </el-form> <el-table :data="tableData" style="width: 100%"> <el-table-column label="ID" width="180"> <template slot-scope="scope"> <i class="el-icon-time"></i> <span style="margin-left: 10px">{{ scope.row.id }}</span> </template> </el-table-column> <el-table-column label="用户编号" width="180"> <template slot-scope="scope"> <span style="margin-left: 10px">{{ scope.row.userCode }}</span> </template> </el-table-column> <el-table-column label="用户姓名" width="180"> <template slot-scope="scope"> <span style="margin-left: 10px">{{ scope.row.userName }}</span> </template> </el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <el-button size="mini" @click="handleEdit(scope.$index, scope.row)">编辑</el-button> <el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button> </template> </el-table-column> </el-table> <!-- 增加用户--> <el-dialog title="增加用户" :visible.sync="dialogFormVisible"> <el-form :model="form" :rules="rules" ref="form"> <el-form-item label="用户编号" prop="userCode"> <el-input v-model="form.userCode" autocomplete="off" maxlength="30" show-word-limit></el-input> </el-form-item> <el-form-item label="用户名称"> <el-input v-model="form.userName" autocomplete="off" maxlength="30" show-word-limit></el-input> </el-form-item> <el-form-item label="密 码"> <el-input v-model="form.password" autocomplete="off" maxlength="30" show-word-limit></el-input> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible = false">取 消</el-button> <el-button type="primary" @click="submitForm">确 定</el-button> </div> </el-dialog> <!-- 分配角色--> <el-dialog title="分配角色" :visible.sync="dialogFormRoleVisible"> <el-form :model="roleForm" :rules="roleRules" ref="roleForm"> <el-autocomplete popper-class="my-autocomplete" v-model="state" :fetch-suggestions="querySearch" placeholder="请输入内容" @select="handleSelect"> <i class="el-icon-edit el-input__icon" slot="suffix" @click="handleIconClick"> </i> <template slot-scope="{ item }"> <div class="name">{{ item.value }}</div> <span class="addr">{{ item.address }}</span> </template> </el-autocomplete> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormRoleVisible = false">取 消</el-button> <el-button type="primary" @click="submitFormRole">确 定</el-button> </div> </el-dialog> </div> </template> <script> export default { data () { return { tableData: [], dialogFormVisible: false, dialogFormRoleVisible: false, form: { userCode: '', userName: '', password: '' }, rules: { userCode: [ {required: true, message: '请输入用户编号', trigger: 'blur'} ] }, roleForm: {}, restaurants: [], state: '' } }, methods: { handleEdit (index, row) { console.log(index, row) }, handleDelete (index, row) { console.log(index, row) }, findAll () { const _this = this this.$axios({ method: 'post', url: '/user/findAll' }).then(function (result) { _this.tableData = result.data }).catch(function (err) { console.log(err) }) }, submitForm: function () { debugger this.$refs['form'].validate((valid) => { if (valid) { this.dialogFormVisible = false this.$axios({ method: 'post', params: this.form, url: '/user/insert' }) } else { return false } }) }, submitFormRole: function () { debugger const self = this this.$refs['roleForm'].validate((valid) => { if (valid) { // this.dialogFormRoleVisible = false this.$axios({ method: 'post', params: this.form, url: '/user/setRole' }).then(function (response) { self.$message(response.data) }) } else { return false } }) }, querySearch (queryString, cb) { var restaurants = this.restaurants var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants // 调用 callback 返回建议列表的数据 cb(results) }, createFilter (queryString) { return (restaurant) => { return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0) } }, loadAll () { return [ {'value': '三全鲜食(北新泾店)', 'address': '长宁区新渔路144号'}, {'value': 'Hot honey 首尔炸鸡(仙霞路)', 'address': '上海市长宁区淞虹路661号'}, {'value': '新旺角茶餐厅', 'address': '上海市普陀区真北路988号创邑金沙谷6号楼113'}, {'value': '泷千家(天山西路店)', 'address': '天山西路438号'}, {'value': '胖仙女纸杯蛋糕(上海凌空店)', 'address': '上海市长宁区金钟路968号1幢18号楼一层商铺18-101'}, {'value': '贡茶', 'address': '上海市长宁区金钟路633号'}, {'value': '豪大大香鸡排超级奶爸', 'address': '上海市嘉定区曹安公路曹安路1685号'}, {'value': '茶芝兰(奶茶,手抓饼)', 'address': '上海市普陀区同普路1435号'}, {'value': '十二泷町', 'address': '上海市北翟路1444弄81号B幢-107'}, {'value': '星移浓缩咖啡', 'address': '上海市嘉定区新郁路817号'}, {'value': '阿姨奶茶/豪大大', 'address': '嘉定区曹安路1611号'}, {'value': '新麦甜四季甜品炸鸡', 'address': '嘉定区曹安公路2383弄55号'}, {'value': 'Monica摩托主题咖啡店', 'address': '嘉定区江桥镇曹安公路2409号1F,2383弄62号1F'}, {'value': '浮生若茶(凌空soho店)', 'address': '上海长宁区金钟路968号9号楼地下一层'}, {'value': 'NONO JUICE 鲜榨果汁', 'address': '上海市长宁区天山西路119号'}, {'value': 'CoCo都可(北新泾店)', 'address': '上海市长宁区仙霞西路'}, {'value': '快乐柠檬(神州智慧店)', 'address': '上海市长宁区天山西路567号1层R117号店铺'}, {'value': 'Merci Paul cafe', 'address': '上海市普陀区光复西路丹巴路28弄6号楼819'}, {'value': '猫山王(西郊百联店)', 'address': '上海市长宁区仙霞西路88号第一层G05-F01-1-306'}, {'value': '枪会山', 'address': '上海市普陀区棕榈路'}, {'value': '纵食', 'address': '元丰天山花园(东门) 双流路267号'}, {'value': '钱记', 'address': '上海市长宁区天山西路'}, {'value': '壹杯加', 'address': '上海市长宁区通协路'}, {'value': '唦哇嘀咖', 'address': '上海市长宁区新泾镇金钟路999号2幢(B幢)第01层第1-02A单元'}, {'value': '爱茜茜里(西郊百联)', 'address': '长宁区仙霞西路88号1305室'}, {'value': '爱茜茜里(近铁广场)', 'address': '上海市普陀区真北路818号近铁城市广场北区地下二楼N-B2-O2-C商铺'}, {'value': '鲜果榨汁(金沙江路和美广店)', 'address': '普陀区金沙江路2239号金沙和美广场B1-10-6'}, {'value': '开心丽果(缤谷店)', 'address': '上海市长宁区威宁路天山路341号'}, {'value': '超级鸡车(丰庄路店)', 'address': '上海市嘉定区丰庄路240号'}, {'value': '妙生活果园(北新泾店)', 'address': '长宁区新渔路144号'}, {'value': '香宜度麻辣香锅', 'address': '长宁区淞虹路148号'}, {'value': '凡仔汉堡(老真北路店)', 'address': '上海市普陀区老真北路160号'}, {'value': '港式小铺', 'address': '上海市长宁区金钟路968号15楼15-105室'}, {'value': '蜀香源麻辣香锅(剑河路店)', 'address': '剑河路443-1'}, {'value': '北京饺子馆', 'address': '长宁区北新泾街道天山西路490-1号'}, {'value': '饭典*新简餐(凌空SOHO店)', 'address': '上海市长宁区金钟路968号9号楼地下一层9-83室'}, {'value': '焦耳·川式快餐(金钟路店)', 'address': '上海市金钟路633号地下一层甲部'}, {'value': '动力鸡车', 'address': '长宁区仙霞西路299弄3号101B'}, {'value': '浏阳蒸菜', 'address': '天山西路430号'}, {'value': '四海游龙(天山西路店)', 'address': '上海市长宁区天山西路'}, {'value': '樱花食堂(凌空店)', 'address': '上海市长宁区金钟路968号15楼15-105室'}, {'value': '壹分米客家传统调制米粉(天山店)', 'address': '天山西路428号'}, {'value': '福荣祥烧腊(平溪路店)', 'address': '上海市长宁区协和路福泉路255弄57-73号'}, {'value': '速记黄焖鸡米饭', 'address': '上海市长宁区北新泾街道金钟路180号1层01号摊位'}, {'value': '红辣椒麻辣烫', 'address': '上海市长宁区天山西路492号'}, {'value': '(小杨生煎)西郊百联餐厅', 'address': '长宁区仙霞西路88号百联2楼'}, {'value': '阳阳麻辣烫', 'address': '天山西路389号'}, {'value': '南拳妈妈龙虾盖浇饭', 'address': '普陀区金沙江路1699号鑫乐惠美食广场A13'} ] }, handleSelect (item) { console.log(item) }, handleIconClick (ev) { console.log(ev) } }, mounted () { this.restaurants = this.loadAll() } } </script> <style> .my-autocomplete { li { line-height: normal; padding: 7px; .name { text-overflow: ellipsis; overflow: hidden; } .addr { font-size: 12px; color: #b4b4b4; } .highlighted .addr { color: #ddd; } } } </style> 后端权限调用/** * @author laughing * @date 2020/10/13 * @site https://www.xiangcaowuyu.net */ @RestController @RequestMapping("user") public class UserController { @Resource ShiroUserService shiroUserService; /** * 查找所有用户 * @return */ @RequiresPermissions("user:find") @RequestMapping("/findAll") public List<ShiroUser> findAll() { return shiroUserService.findAll(); } /** * 查找所有用户 * @return */ @RequiresPermissions("user:find") @RequestMapping("/insert") public int save(ShiroUser shiroUser) { return shiroUserService.insert(shiroUser); } }
2020年10月14日
1,301 阅读
0 评论
1 点赞
2020-10-11
超详细Vue+Spring Boot整合Shiro前后端分离架构-Shiro后端设计(三)
Shiro实战shiro实战内容包括三个部分:(1)shiro后台表结构,用于存储shiro对应的用户、角色、权限及关联关系。(2)后端代码,及基于shiro配置用户、角色、权限及对应关系以及登录、认证。(3)前端代码,维护shiro信息及登录、认证。这篇博文我们介绍第二部分,即后端设计后端代码后端数据库访问用的mybatis及lombok插件。添加依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 热部署--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <!-- mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${spring.shiro.version}</version> </dependency> <dependency> <!--session持久化插件--> <groupId>org.crazycake</groupId> <artifactId>shiro-redis</artifactId> <version>${shiro.redis.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <!-- 导入配置文件处理器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies>配置文件主要配置数据库连接、mybatis及shiro信息。spring: datasource: url: jdbc:mysql://localhost:3306/shiro?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver mybatis: type-aliases-package: net.xiangcaowuyu.shiro.entity mapper-locations: classpath:mapper/*.xml shiro: user: loginUrl: /shiro/login/** unauthorizedUrl: /shiro/logout indexUrl: /shiro/index captchaEnabled: true captchaType: math redis: host: localhost port: 6379 cookie: domain: path: / httpOnly: false maxAge: 72 #Hours,利用 cookie 免登录。 secure: false session: expireTime: 72 #Hours dbSyncPeriod: 1 validationInterval: 10 maxSession: -1 kickoutAfter: false server: port: 8888 #配置跨域 cors: allowedOrigin: - http://localhost:8080 allowCredentials: true allowHeaders: - \* allowMethods: - GET - POST - PUT - DELETE - OPTIONS maxAge: 7200 path: /**mybatis代码配置完成后,我们先生成mybatis代码,完成mybatis的操作。我使用Free MyBatis plugin插件生成一些代码。mybatis实体ShiroUser.java@Data public class ShiroUser implements Serializable { private static final long serialVersionUID = 1L; private Long id; private String userCode; private String userName; private String password; private Set<ShiroRole> shiroRoleSet; }ShiroRole.java@Data public class ShiroRole implements Serializable { private static final long serialVersionUID = 1L; private Long id; private String roleCode; private String roleName; private Set<ShiroPermission> shiroPermissionSet; }ShiroPermission.java@Data public class ShiroPermission implements Serializable { private Long id; private String permissionCode; private String permissionName; private static final long serialVersionUID = 1L; }ShiroUserRole.java@Data public class ShiroUserRole implements Serializable { private Long id; private Long userId; private Long roleId; private static final long serialVersionUID = 1L; }ShiroRolePermission.java@Data public class ShiroRolePermission implements Serializable { private Long id; private Long roleId; private Long permissionId; private static final long serialVersionUID = 1L; }数据访问接口ShiroUserMapper.java@Mapper public interface ShiroUserMapper { int deleteByPrimaryKey(Long id); int insert(ShiroUser record); int insertSelective(ShiroUser record); /** * 根据主键获取用户信息 * @param id Id * @return 用户 */ ShiroUser selectByPrimaryKey(Long id); int updateByPrimaryKeySelective(ShiroUser record); int updateByPrimaryKey(ShiroUser record); /** * 根据用户编号获取 * @param userCode 用户编号 * @return 用户 */ ShiroUser findUserByUserCode(String userCode); }ShiroRoleMapper.java@Mapper public interface ShiroRoleMapper { int deleteByPrimaryKey(Long id); int insert(ShiroRole record); int insertSelective(ShiroRole record); ShiroRole selectByPrimaryKey(Long id); int updateByPrimaryKeySelective(ShiroRole record); int updateByPrimaryKey(ShiroRole record); /** * 根据用户编号获取其角色列表 * @param userCode 用户编号 * @return 权限列表 */ Set<ShiroRole> findByUserCode(String userCode); }ShiroPermissionMapper.java@Mapper public interface ShiroPermissionMapper { int deleteByPrimaryKey(Long id); int insert(ShiroPermission record); int insertSelective(ShiroPermission record); ShiroPermission selectByPrimaryKey(Long id); int updateByPrimaryKeySelective(ShiroPermission record); int updateByPrimaryKey(ShiroPermission record); /** * 获取角色对应权限 * @param roleId 角色Id * @return 权限集合 */ Set<ShiroPermission> findByRoleId(long roleId); }ShiroUserRoleMapper.java@Mapper public interface ShiroUserRoleMapper { int deleteByPrimaryKey(Long id); int insert(ShiroUserRole record); int insertSelective(ShiroUserRole record); ShiroUserRole selectByPrimaryKey(Long id); int updateByPrimaryKeySelective(ShiroUserRole record); int updateByPrimaryKey(ShiroUserRole record); }ShiroRolePermissionMapper.java@Mapper public interface ShiroRolePermissionMapper { int deleteByPrimaryKey(Long id); int insert(ShiroRolePermission record); int insertSelective(ShiroRolePermission record); ShiroRolePermission selectByPrimaryKey(Long id); int updateByPrimaryKeySelective(ShiroRolePermission record); int updateByPrimaryKey(ShiroRolePermission record); }xml文件ShiroUserMapper.xml<?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="net.xiangcaowuyu.shiro.mapper.ShiroUserMapper"> <resultMap id="BaseResultMap" type="net.xiangcaowuyu.shiro.entity.ShiroUser"> <id column="id" jdbcType="BIGINT" property="id"/> <result column="userCode" jdbcType="VARCHAR" property="userCode"/> <result column="userName" jdbcType="VARCHAR" property="userName"/> <result column="password" jdbcType="VARCHAR" property="password"/> </resultMap> <sql id="Base_Column_List"> id, userCode, userName, `password` </sql> <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from Shiro_User where id = #{id,jdbcType=BIGINT} </select> <select id="findUserByUserCode" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from Shiro_User where userCode = #{userCode} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Long"> delete from Shiro_User where id = #{id,jdbcType=BIGINT} </delete> <insert id="insert" keyColumn="id" keyProperty="id" parameterType="net.xiangcaowuyu.shiro.entity.ShiroUser" useGeneratedKeys="true"> insert into Shiro_User (userCode, userName, `password`) values (#{usercode,jdbcType=VARCHAR}, #{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}) </insert> <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="net.xiangcaowuyu.shiro.entity.ShiroUser" useGeneratedKeys="true"> insert into Shiro_User <trim prefix="(" suffix=")" suffixOverrides=","> <if test="userCode != null"> userCode, </if> <if test="userName != null"> userName, </if> <if test="password != null"> `password`, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="userCode != null"> #{usercode,jdbcType=VARCHAR}, </if> <if test="userName != null"> #{username,jdbcType=VARCHAR}, </if> <if test="password != null"> #{password,jdbcType=VARCHAR}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="net.xiangcaowuyu.shiro.entity.ShiroUser"> update Shiro_User <set> <if test="userCode != null"> userCode = #{usercode,jdbcType=VARCHAR}, </if> <if test="userName != null"> userName = #{username,jdbcType=VARCHAR}, </if> <if test="password != null"> `password` = #{password,jdbcType=VARCHAR}, </if> </set> where id = #{id,jdbcType=BIGINT} </update> <update id="updateByPrimaryKey" parameterType="net.xiangcaowuyu.shiro.entity.ShiroUser"> update Shiro_User set userCode = #{usercode,jdbcType=VARCHAR}, userName = #{username,jdbcType=VARCHAR}, `password` = #{password,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} </update> </mapper>ShiroRoleMapper.xml<?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="net.xiangcaowuyu.shiro.mapper.ShiroRoleMapper"> <resultMap id="BaseResultMap" type="net.xiangcaowuyu.shiro.entity.ShiroRole"> <id column="id" jdbcType="BIGINT" property="id" /> <result column="roleCode" jdbcType="VARCHAR" property="roleCode" /> <result column="roleName" jdbcType="VARCHAR" property="roleName" /> </resultMap> <sql id="Base_Column_List"> id, roleCode, roleName </sql> <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from Shiro_Role where id = #{id,jdbcType=BIGINT} </select> <select id="findByUserCode" resultType="net.xiangcaowuyu.shiro.entity.ShiroRole"> select Shiro_Role.* from Shiro_Role inner join Shiro_User_Role SUR on Shiro_Role.id = SUR.roleId inner join Shiro_User SU on SUR.userId = SU.id where su.userCode = #{userCode} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Long"> delete from Shiro_Role where id = #{id,jdbcType=BIGINT} </delete> <insert id="insert" keyColumn="id" keyProperty="id" parameterType="net.xiangcaowuyu.shiro.entity.ShiroRole" useGeneratedKeys="true"> insert into Shiro_Role (roleCode, roleName) values (#{rolecode,jdbcType=VARCHAR}, #{rolename,jdbcType=VARCHAR}) </insert> <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="net.xiangcaowuyu.shiro.entity.ShiroRole" useGeneratedKeys="true"> insert into Shiro_Role <trim prefix="(" suffix=")" suffixOverrides=","> <if test="roleCode != null"> roleCode, </if> <if test="roleName != null"> roleName, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="roleCode != null"> #{rolecode,jdbcType=VARCHAR}, </if> <if test="roleName != null"> #{rolename,jdbcType=VARCHAR}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="net.xiangcaowuyu.shiro.entity.ShiroRole"> update Shiro_Role <set> <if test="roleCode != null"> roleCode = #{rolecode,jdbcType=VARCHAR}, </if> <if test="roleName != null"> roleName = #{rolename,jdbcType=VARCHAR}, </if> </set> where id = #{id,jdbcType=BIGINT} </update> <update id="updateByPrimaryKey" parameterType="net.xiangcaowuyu.shiro.entity.ShiroRole"> update Shiro_Role set roleCode = #{rolecode,jdbcType=VARCHAR}, roleName = #{rolename,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} </update> </mapper>ShiroPermissionMapper.xml<?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="net.xiangcaowuyu.shiro.mapper.ShiroPermissionMapper"> <resultMap id="BaseResultMap" type="net.xiangcaowuyu.shiro.entity.ShiroPermission"> <id column="id" jdbcType="BIGINT" property="id"/> <result column="permissionCode" jdbcType="VARCHAR" property="permissionCode"/> <result column="permissionName" jdbcType="VARCHAR" property="permissionName"/> </resultMap> <sql id="Base_Column_List"> id, permissionCode, permissionName </sql> <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from Shiro_Permission where id = #{id,jdbcType=BIGINT} </select> <select id="findByRoleId" resultType="net.xiangcaowuyu.shiro.entity.ShiroPermission"> select Shiro_Permission.* from Shiro_Permission inner join Shiro_Role_Permission SRP on Shiro_Permission.id = SRP.permissionId where SRP.roleId = #{roleId} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Long"> delete from Shiro_Permission where id = #{id,jdbcType=BIGINT} </delete> <insert id="insert" keyColumn="id" keyProperty="id" parameterType="net.xiangcaowuyu.shiro.entity.ShiroPermission" useGeneratedKeys="true"> insert into Shiro_Permission (permissionCode, permissionName) values (#{permissioncode,jdbcType=VARCHAR}, #{permissionname,jdbcType=VARCHAR}) </insert> <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="net.xiangcaowuyu.shiro.entity.ShiroPermission" useGeneratedKeys="true"> insert into Shiro_Permission <trim prefix="(" suffix=")" suffixOverrides=","> <if test="permissionCode != null"> permissionCode, </if> <if test="permissionName != null"> permissionName, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="permissionCode != null"> #{permissioncode,jdbcType=VARCHAR}, </if> <if test="permissionName != null"> #{permissionname,jdbcType=VARCHAR}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="net.xiangcaowuyu.shiro.entity.ShiroPermission"> update Shiro_Permission <set> <if test="permissionCode != null"> permissionCode = #{permissioncode,jdbcType=VARCHAR}, </if> <if test="permissionName != null"> permissionName = #{permissionname,jdbcType=VARCHAR}, </if> </set> where id = #{id,jdbcType=BIGINT} </update> <update id="updateByPrimaryKey" parameterType="net.xiangcaowuyu.shiro.entity.ShiroPermission"> update Shiro_Permission set permissionCode = #{permissioncode,jdbcType=VARCHAR}, permissionName = #{permissionname,jdbcType=VARCHAR} where id = #{id,jdbcType=BIGINT} </update> </mapper>ShiroUserRoleMapper.xml<?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="net.xiangcaowuyu.shiro.mapper.ShiroUserRoleMapper"> <resultMap id="BaseResultMap" type="net.xiangcaowuyu.shiro.entity.ShiroUserRole"> <id column="id" jdbcType="BIGINT" property="id" /> <result column="userId" jdbcType="BIGINT" property="userId" /> <result column="roleId" jdbcType="BIGINT" property="roleId" /> </resultMap> <sql id="Base_Column_List"> id, userId, roleId </sql> <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from Shiro_User_Role where id = #{id,jdbcType=BIGINT} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Long"> delete from Shiro_User_Role where id = #{id,jdbcType=BIGINT} </delete> <insert id="insert" keyColumn="id" keyProperty="id" parameterType="net.xiangcaowuyu.shiro.entity.ShiroUserRole" useGeneratedKeys="true"> insert into Shiro_User_Role (userId, roleId) values (#{userid,jdbcType=BIGINT}, #{roleid,jdbcType=BIGINT}) </insert> <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="net.xiangcaowuyu.shiro.entity.ShiroUserRole" useGeneratedKeys="true"> insert into Shiro_User_Role <trim prefix="(" suffix=")" suffixOverrides=","> <if test="userId != null"> userId, </if> <if test="roleId != null"> roleId, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="userid != null"> #{userid,jdbcType=BIGINT}, </if> <if test="roleId != null"> #{roleid,jdbcType=BIGINT}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="net.xiangcaowuyu.shiro.entity.ShiroUserRole"> update Shiro_User_Role <set> <if test="userId != null"> userId = #{userid,jdbcType=BIGINT}, </if> <if test="roleId != null"> roleId = #{roleid,jdbcType=BIGINT}, </if> </set> where id = #{id,jdbcType=BIGINT} </update> <update id="updateByPrimaryKey" parameterType="net.xiangcaowuyu.shiro.entity.ShiroUserRole"> update Shiro_User_Role set userId = #{userid,jdbcType=BIGINT}, roleId = #{roleid,jdbcType=BIGINT} where id = #{id,jdbcType=BIGINT} </update> </mapper>ShiroRolePermissionMapper.xml<?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="net.xiangcaowuyu.shiro.mapper.ShiroRolePermissionMapper"> <resultMap id="BaseResultMap" type="net.xiangcaowuyu.shiro.entity.ShiroRolePermission"> <id column="id" jdbcType="BIGINT" property="id" /> <result column="roleId" jdbcType="BIGINT" property="roleId" /> <result column="permissionId" jdbcType="BIGINT" property="permissionId" /> </resultMap> <sql id="Base_Column_List"> id, roleId, permissionId </sql> <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from Shiro_Role_Permission where id = #{id,jdbcType=BIGINT} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Long"> delete from Shiro_Role_Permission where id = #{id,jdbcType=BIGINT} </delete> <insert id="insert" keyColumn="id" keyProperty="id" parameterType="net.xiangcaowuyu.shiro.entity.ShiroRolePermission" useGeneratedKeys="true"> insert into Shiro_Role_Permission (roleId, permissionId) values (#{roleid,jdbcType=BIGINT}, #{permissionid,jdbcType=BIGINT}) </insert> <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="net.xiangcaowuyu.shiro.entity.ShiroRolePermission" useGeneratedKeys="true"> insert into Shiro_Role_Permission <trim prefix="(" suffix=")" suffixOverrides=","> <if test="roleId != null"> roleId, </if> <if test="permissionId != null"> permissionId, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="roleId != null"> #{roleid,jdbcType=BIGINT}, </if> <if test="permissionId != null"> #{permissionid,jdbcType=BIGINT}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="net.xiangcaowuyu.shiro.entity.ShiroRolePermission"> update Shiro_Role_Permission <set> <if test="roleId != null"> roleId = #{roleid,jdbcType=BIGINT}, </if> <if test="permissionId != null"> permissionId = #{permissionid,jdbcType=BIGINT}, </if> </set> where id = #{id,jdbcType=BIGINT} </update> <update id="updateByPrimaryKey" parameterType="net.xiangcaowuyu.shiro.entity.ShiroRolePermission"> update Shiro_Role_Permission set roleId = #{roleid,jdbcType=BIGINT}, permissionId = #{permissionid,jdbcType=BIGINT} where id = #{id,jdbcType=BIGINT} </update> </mapper>服务层服务层这里忽略了接口,有需要的可以查看源代码ShiroUserServiceImpl.java@Service public class ShiroUserServiceImpl implements ShiroUserService { @Resource ShiroUserMapper shiroUserMapper; @Resource ShiroRoleService shiroRoleService; /** * 根据用户编号获取 * * @param userCode 用户编号 * @return 用户 */ @Override public ShiroUser findUserByUserCode(String userCode) { ShiroUser shiroUser = shiroUserMapper.findUserByUserCode(userCode); Set<ShiroRole> shiroRoleSet = shiroRoleService.findByUserCode(userCode); shiroUser.setShiroRoleSet(shiroRoleSet); return shiroUser; } }ShiroRoleServiceImpl.java@Service public class ShiroRoleServiceImpl implements ShiroRoleService { @Resource ShiroRoleMapper shiroRoleMapper; @Resource ShiroPermissionService shiroPermissionService; /** * 根据用户编号获取其角色列表 * * @param userCode 用户编号 * @return 权限列表 */ @Override public Set<ShiroRole> findByUserCode(String userCode) { Set<ShiroRole> shiroRoleSet = shiroRoleMapper.findByUserCode(userCode); for (ShiroRole shiroRole : shiroRoleSet) { Set<ShiroPermission> shiroPermissionSet = shiroPermissionService.findByRoleId(shiroRole.getId()); shiroRole.setShiroPermissionSet(shiroPermissionSet); } return shiroRoleSet; } } ShiroPermissionServiceImpl.java@Service public class ShiroPermissionServiceImpl implements ShiroPermissionService { @Resource ShiroPermissionMapper shiroPermissionMapper; /** * 获取角色对应权限 * * @param roleId 角色Id * @return 权限集合 */ @Override public Set<ShiroPermission> findByRoleId(long roleId) { return shiroPermissionMapper.findByRoleId(roleId); } }ShiroServiceImpl.java@Service public class ShiroServiceImpl implements ShiroService { @Resource ShiroUserService shiroUserService; /** * 根据用户编号获取 * * @param userCode 用户编号 * @return 用户 */ @Override public ShiroUser findUserByCode(String userCode) { return shiroUserService.findUserByUserCode(userCode); } }shiro配置常量配置AuthenConst常量类,用于配置加密方式、盐、加密次数等信息。/** * @author laughing * @date 2020/10/12 * @site https://www.xiangcaowuyu.net */ public class AuthenConst { public static final String SALT = "XiangCaoWuYu"; public static final String HASH_ALGORITHM_NAME = "MD5"; public static final int HASH_INTERACTIONS = 1024; public static final String OPTION_REQUEST_NAME = "OPTIONS"; }property映射/** * @author laughing * @date 2020/10/11 * @site https://www.xiangcaowuyu.net */ @ConfigurationProperties(prefix = "shiro") @Configuration @Data public class ShiroProperty { private User user; private Redis redis; } @Data class User{ private String loginUrl; private String unauthorizedUrl; private String indexUrl; private boolean captchaEnabled; private String captchaType; } @Data class Redis{ private String host; private long port; }自定义realm/** * @author laughing * @date 2020/10/11 * @site https://www.lisen.org */ public class MyAuthenRealm extends AuthorizingRealm { @Resource ShiroService shiroService; /** * 认证 * * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; String userCode = token.getUsername(); ShiroUser shiroUser = shiroService.findUserByCode(userCode); if (shiroUser == null) { throw new AuthenticationException("账户不存在"); } //自定义盐值 // ByteSource salt = ByteSource.Util.bytes(AuthenConst.SALT); // String password = new SimpleHash(AuthenConst.HASH_ALGORITHM_NAME, new String(token.getPassword()), AuthenConst.SALT, AuthenConst.HASH_INTERACTIONS).toHex(); if (!new String(token.getPassword()).equals(shiroUser.getPassword())) { throw new IncorrectCredentialsException("账户密码不正确"); } // Subject subject = SecurityUtils.getSubject(); // ShiroUserVO shiroUserVO = new ShiroUserVO(); // BeanUtils.copyProperties(shiroUserVO,shiroUser); // shiroUserVO.setSessionId(subject.getSession().getId().toString()); return new SimpleAuthenticationInfo(userCode, shiroUser.getPassword(), getName()); } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { if (StringUtils.isEmpty(principalCollection)) { return null; } String userCode = principalCollection.getPrimaryPrincipal().toString(); ShiroUser shiroUser = shiroService.findUserByCode(userCode); SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); for (ShiroRole shiroRole : shiroUser.getShiroRoleSet()) { simpleAuthorizationInfo.addRole(shiroRole.getRoleCode()); for (ShiroPermission shiroPermission : shiroRole.getShiroPermissionSet()) { simpleAuthorizationInfo.addStringPermission(shiroPermission.getPermissionCode()); } } return simpleAuthorizationInfo; } }禁止未登录跳转因为我们是前后端分离项目,如果未登陆跳转的话,前端无法捕捉重定向后的消息,所以我们需要配置禁止未登录跳转。/** * 对于跨域的POST请求,浏览器发起POST请求前都会发送一个OPTIONS请求已确定服务器是否可用, * OPTIONS请求通过后继续执行POST请求,而shiro自带的权限验证是无法处理OPTIONS请求的, * 所以这里需要重写isAccessAllowed方法 * @author laughing * @date 2020/10/12 * @site https://www.xiangcaowuyu.net */ public class ShiroFormAuthenticationFilter extends FormAuthenticationFilter { private static final Logger log = LoggerFactory.getLogger(ShiroFormAuthenticationFilter.class); /** * 重写是否允许访问 * @param request * @param response * @param mappedValue * @return */ @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { HttpServletRequest httpServletRequest = WebUtils.toHttp(request); if(AuthenConst.OPTION_REQUEST_NAME.equals(httpServletRequest.getMethod())){ return true; } return super.isAccessAllowed(request, response, mappedValue); } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws JSONException { PrintWriter out = null; HttpServletResponse res = (HttpServletResponse) response; try { res.setCharacterEncoding("UTF-8"); res.setContentType("application/json"); out = response.getWriter(); out.println("未授权"); } catch (Exception e) { } finally { if (null != out) { out.flush(); out.close(); } } return false; } } shiro配置/** * @author laughing * @date 2020/10/11 * @site https://www.xiangcaowuyu.net */ @Configuration public class ShiroConfig { /** * 自定义验证 * @return */ @Bean MyAuthenRealm myAuthenRealm() { MyAuthenRealm myAuthenRealm = new MyAuthenRealm(); // myAuthenRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return myAuthenRealm; } @Resource ShiroProperty shiroProperty; /** * 权限管理,配置主要是Realm的管理认证 * * @return */ @Bean public SecurityManager securityManager() { DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); defaultWebSecurityManager.setRealm(myAuthenRealm()); // 自定义session管理 使用redis defaultWebSecurityManager.setSessionManager(sessionManager()); return defaultWebSecurityManager; } /** * Filter工厂,设置对应的过滤条件和跳转条件 */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean() { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager()); Map<String, String> filterChainDefinitionMap = new HashMap<>(); //登出 filterChainDefinitionMap.put(shiroProperty.getUser().getUnauthorizedUrl(), "logout"); //登录 shiroFilterFactoryBean.setLoginUrl(shiroProperty.getUser().getLoginUrl()); //首页 shiroFilterFactoryBean.setSuccessUrl(shiroProperty.getUser().getIndexUrl()); filterChainDefinitionMap.put("/", "anon"); filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put(shiroProperty.getUser().getLoginUrl(), "anon"); filterChainDefinitionMap.put(shiroProperty.getUser().getUnauthorizedUrl(), "anon"); filterChainDefinitionMap.put("/error", "anon"); filterChainDefinitionMap.put("/**", "authc"); LinkedHashMap<String, Filter> filtsMap = new LinkedHashMap<>(); shiroFilterFactoryBean.setFilters(filtsMap); // 这里使用自定义的filter,禁止未登陆跳转 filtsMap.put("authc", new ShiroFormAuthenticationFilter()); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * 加入注解的使用,不加入这个注解不生效 * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } @Bean @ConditionalOnMissingBean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); return defaultAdvisorAutoProxyCreator; } // @Bean // public HashedCredentialsMatcher hashedCredentialsMatcher() { // HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); // //指定加密方式 // credentialsMatcher.setHashAlgorithmName(AuthenConst.HASH_ALGORITHM_NAME); // //加密次数 // credentialsMatcher.setHashIterations(AuthenConst.HASH_INTERACTIONS); // //此处的设置,true加密用的hex编码,false用的base64编码 // credentialsMatcher.setStoredCredentialsHexEncoded(true); // return credentialsMatcher; // } @Bean public SessionManager sessionManager(){ MySessionManager mySessionManager = new MySessionManager(); // 取消登陆跳转URL后面的jsessionid参数 mySessionManager.setSessionIdUrlRewritingEnabled(false); mySessionManager.setSessionDAO(redisSessionDAO()); // 不过期 mySessionManager.setGlobalSessionTimeout(-1); return mySessionManager; } @Bean public RedisManager redisManager(){ RedisManager redisManager = new RedisManager(); redisManager.setHost(shiroProperty.getRedis().getHost()+":"+shiroProperty.getRedis().getPort()); redisManager.setDatabase(0); return redisManager; } @Bean public RedisSessionDAO redisSessionDAO(){ RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); return redisSessionDAO; } }全局异常@RestControllerAdvice public class MyAuthorizationException { @ExceptionHandler(UnauthorizedException.class) public String authorization(UnauthorizedException myAuthorizationException) { return "没有"+myAuthorizationException.toString()+"权限"; } }测试类/** * @author laughing * @date 2020/10/11 * @site https://www.lisen.org */ @RestController @RequestMapping("/shiro") public class ShiroController { private final Logger logger = LoggerFactory.getLogger(ShiroController.class); @Resource ShiroService shiroService; @RequestMapping("/findUserByUserCode") public ShiroUser findUserByUserCode() { String userCode = "lisen"; ShiroUser shiroUser = shiroService.findUserByCode(userCode); return shiroUser; } @RequestMapping("/login") public String login(@RequestParam("userCode") String userCode, @RequestParam("password") String password) { // password = new SimpleHash(AuthenConst.HASH_ALGORITHM_NAME,password,AuthenConst.SALT,AuthenConst.HASH_INTERACTIONS).toHex(); UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userCode, password); Subject subject = SecurityUtils.getSubject(); try { //进行验证,这里可以捕获异常,然后返回对应信息 subject.login(usernamePasswordToken); } catch (UnknownAccountException e) { logger.error("用户名不存在!", e); return "用户名不存在!"; } catch (AuthenticationException e) { logger.error("账号或密码错误!", e); return "账号或密码错误!"; } catch (AuthorizationException e) { logger.error("没有权限!", e); return "没有权限"; } return "login success"; } @RequestMapping("/index") public String index(String userCode, String password) { return "index"; } @RequiresPermissions("permission") @RequestMapping("/permission") public String permission() { return "permission"; } @RequiresPermissions("dept:add") @RequestMapping("/deptadd") public String deptAdd() { return "dept:add"; } @RequestMapping("/nopermission") public String noPermission() { return "noPermission"; } }表数据因为我们这里还没设计授权的界面,所以临时在数据库插入了测试数据Shiro_UserINSERT INTO `shiro`.`Shiro_User`(`id`, `userCode`, `userName`, `password`) VALUES (1, 'lisen', '李森', 'f5e617c6615d53ae33b9d80a2087e264');Shiro_RoleINSERT INTO `shiro`.`Shiro_Role`(`id`, `roleCode`, `roleName`) VALUES (1, 'admin', '管理员');Shiro_PermissionINSERT INTO `shiro`.`Shiro_Permission`(`id`, `permissionCode`, `permissionName`) VALUES (1, 'dept:add', '部门-增加');Shiro_User_RoleINSERT INTO `shiro`.`Shiro_User_Role`(`id`, `userId`, `roleId`) VALUES (1, 1, 1);Shiro_Role_PermissionINSERT INTO `shiro`.`Shiro_Role_Permission`(`id`, `roleId`, `permissionId`) VALUES (1, 1, 1);
2020年10月11日
1,596 阅读
0 评论
0 点赞
2020-10-11
超详细Vue+Spring Boot整合Shiro前后端分离架构-Shiro表结构设计(二)
在超详细Vue+Spring Boot整合Shiro前后端分离架构-Shiro介绍(一)一文中,我们介绍了Shiro的基本概念,本章开始,我们进入Shiro的实现环节。Shiro实战shiro实战内容包括三个部分:(1)shiro后台表结构,用于存储shiro对应的用户、角色、权限及关联关系。(2)后端代码,及基于shiro配置用户、角色、权限及对应关系以及登录、认证。(3)前端代码,维护shiro信息及登录、认证。这篇博文,我们讲解第一部分,及Shiro的表结构设计。表结构设计我这里设计了5张表,分别为Shiro_User,Shiro_Role,Shiro_Permission,Shiro_User_Role,Shiro_Role_Permission。Shiro_UserShiro_User用于存储用户信息,主要涉及字段为用户Id(id)、用户名称(username)及用户密码(password)。shiro由提供的密码加密方式,所以,我们数据库存储的密文为调用shiro加密后的用户密码。CREATE TABLE `Shiro_User` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `userCode` varchar(255) NOT NULL, `userName` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, PRIMARY KEY (`id`) USING BTREE, UNIQUE KEY `id` (`id`) USING BTREE, UNIQUE KEY `code` (`userCode`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;Shiro_RoleShiro_Role用于存储角色,如系统管理员、业务人员等信息。一个用户拥有多个角色,一个角色也可以属于多个用户。CREATE TABLE `Shiro_Role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `roleCode` varchar(255) NOT NULL, `roleName` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, UNIQUE KEY `id` (`id`) USING BTREE, UNIQUE KEY `roleCode` (`roleCode`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;Shiro_PermissionShiro_Permission用于存储权限,一个角色可能有多个权限,一个权限也可能隶属于多个角色。CREATE TABLE `Shiro_Permission` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `permissionCode` varchar(255) NOT NULL, `permissionName` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, UNIQUE KEY `id` (`id`) USING BTREE, UNIQUE KEY `permissionCode` (`permissionCode`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;Shiro_User_RoleShiro_User_Role用于存储用户与角色的关联关系。CREATE TABLE `Shiro_User_Role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `userId` bigint(20) NOT NULL, `roleId` bigint(20) NOT NULL, PRIMARY KEY (`id`), KEY `userId` (`userId`), KEY `roleId` (`roleId`), CONSTRAINT `shiro_user_role_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `Shiro_User` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT `shiro_user_role_ibfk_2` FOREIGN KEY (`roleId`) REFERENCES `Shiro_Role` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;Shiro_Role_PermissionShiro_Role_Permission存储角色与权限的关联关系。CREATE TABLE `Shiro_Role_Permission` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `roleId` bigint(20) NOT NULL, `permissionId` bigint(20) NOT NULL, PRIMARY KEY (`id`), KEY `roleId` (`roleId`), KEY `permissionId` (`permissionId`), CONSTRAINT `shiro_role_permission_ibfk_1` FOREIGN KEY (`roleId`) REFERENCES `Shiro_Role` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT `shiro_role_permission_ibfk_2` FOREIGN KEY (`permissionId`) REFERENCES `Shiro_Permission` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;至此,数据库表结构已经设计完成。
2020年10月11日
1,658 阅读
0 评论
0 点赞
2020-10-10
超详细Vue+Spring Boot整合Shiro前后端分离架构-Shiro介绍(一)
其实网上关于spring boot整合shiro的例子并不少见,为什么我这里要重复\`造轮子\`,因为网上所谓的整合,基本上都是以Demo为主,很少能见到Vue+Spring Boot+Shiro完整整合的例子。Shiro概念Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。三个核心组件:Subject, SecurityManager 和 Realms.Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果系统默认的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
2020年10月10日
2,733 阅读
0 评论
2 点赞