- A+
源码下载地址
https://github.com/Aizhuxueliang/springboot_shiro.git
前提你电脑的安装好这些工具:jdk8、idea、maven、git、mysql
;
shiro的主要概念
- Shiro是一个强大的简单易用的Java安全框架,主要用来更便捷的认证、授权、加密、会话管理、与Web集成、缓存等;
- Shiro使用起来小而简单;
- spring中有spring security ,是一个权限框架,它和spring依赖过于紧密,没有shiro使用简单;
- shiro不依赖于spring,shiro不仅可以实现web应用的权限管理,还可以实现c/s系统,分布式系统权限管理;
在应用程序角度来观察如何使用Shiro完成工作
Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject 都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;
SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager 交互;且它管理着所有Subject;可以看出它是Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;
Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
shiro官方文档
https://shiro.apache.org/architecture.html
前端CDN的方式使用elementUI、vue、vue-router、axios画页面
CDN(内容分发网络)本身是指一种请求资源的方式。说白了就是在本地,通过script头去请求对应的脚本资源的一种方式。我在这里要说的就是直接引用 (demo是直接引用的,注意电脑联网
)或者下载Vue.js和elementUI.js放在本地,进行项目开发的方式。而不是通过npm包管理工具去下载vue包。
cdn方式引入elementui 官方API:https://element.eleme.cn/#/zh-CN/component/installation
cdn方式引入vue-router官方API:https://router.vuejs.org/zh/guide/#html
DEMO的整体流程设计
技术层面了解完就该设计业务流程了,如图:
demo整体结构
如图:
主要功能页面
1、登录
2、用户查询
3、分配角色
4、删除角色
5、新建用户
6、分配权限
7、新建角色
主要代码
com/example/demo/controller/UserCtrl.java
package com.example.demo.controller; import com.example.demo.entity.Role; import com.example.demo.entity.User; import com.example.demo.service.UserService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.Map; /** * 控制层 */ @RestController @RequestMapping("/user") public class UserCtrl { @Autowired private UserService userService; /** * 登录接口 * * @param user user * @return resultMap */ @RequestMapping(value = "/login", method = RequestMethod.POST) public Map<String, Object> login(@RequestBody User user) { //拿到主体 Subject subject = SecurityUtils.getSubject(); try { UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUsername(), user.getPassword()); subject.login(usernamePasswordToken); Object permissions = subject.getSession().getAttribute("permissions"); subject.getSession().removeAttribute("permissions"); return userService.resultMap("permissions", permissions, "token", subject.getSession().getId(), "", ""); }catch (Exception e){ e.printStackTrace(); return userService.resultMap("error", e.getMessage(), "", "", "", ""); } } /** * 获取用户的角色类型 * * @param user user * @return resultMap */ @RequestMapping(value = "/findRoleListByUserId", method = RequestMethod.POST) public Map<String, Object> findRoleListByUserId(@RequestBody User user) { try{ return userService.findRoleListByUserId(user.getId()); }catch (Exception e){ e.printStackTrace(); return userService.resultMap("error", e.getMessage(), "", "", "", ""); } } /** * 获取角色的权限类型 * * @param role role * @return resultMap */ @RequestMapping(value = "/findPermissionListByRoleId", method = RequestMethod.POST) public Map<String, Object> findPermissionListByRoleId(@RequestBody Role role) { try{ return userService.findPermissionListByRoleId(role.getId()); }catch (Exception e){ e.printStackTrace(); return userService.resultMap("error", e.getMessage(), "", "", "", ""); } } /** * 更新角色具有的权限 * * @param role role * @return resultMap */ @RequestMapping(value = "/updateRolePermission", method = RequestMethod.POST) public Map<String, Object> updateRolePermission(@RequestBody Role role) { try{ return userService.updateRolePermission(role); }catch (Exception e){ e.printStackTrace(); return userService.resultMap("error", e.getMessage(), "", "", "", ""); } } /** * 删除角色 * * @param role role * @return resultMap */ @RequestMapping(value = "/removeRole", method = RequestMethod.POST) public Map<String, Object> removeRole(@RequestBody Role role) { try{ return userService.removeRole(role); }catch (Exception e){ e.printStackTrace(); return userService.resultMap("error", e.getMessage(), "", "", "", ""); } } /** * 添加角色 * * @param role role * @return resultMap */ @RequestMapping(value = "/addRole", method = RequestMethod.POST) public Map<String, Object> addRole(@RequestBody Role role) { try{ return userService.addRole(role); }catch (Exception e){ e.printStackTrace(); return userService.resultMap("error", e.getMessage(), "", "", "", ""); } } /** * 更新用户具有的角色 * * @param user user * @return resultMap */ @RequestMapping(value = "/updateUserRole", method = RequestMethod.POST) public Map<String, Object> updateUserRole(@RequestBody User user) { try{ return userService.updateUserRole(user); }catch (Exception e){ e.printStackTrace(); return userService.resultMap("error", e.getMessage(), "", "", "", ""); } } /** * 根据用户提供的条件分页查询用户 * * @param user user * @return resultMap */ @RequestMapping(value = "/queryUserListPage", method = RequestMethod.POST) public Map<String, Object> queryUserListPage(@RequestBody User user) { try{ return userService.queryUserListPage(user); }catch (Exception e){ e.printStackTrace(); return userService.resultMap("error", e.getMessage(), "", "", "", ""); } } /** * 删除用户 * * @param user user * @return resultMap */ @RequestMapping(value = "/removeUser", method = RequestMethod.POST) public Map<String, Object> removeUser(@RequestBody User user) { try{ return userService.removeUser(user); }catch (Exception e){ e.printStackTrace(); return userService.resultMap("error", e.getMessage(), "", "", "", ""); } } /** * 新增用户 * * @param user user * @return resultMap */ @RequestMapping(value = "/insertUser", method = RequestMethod.POST) public Map<String, Object> insertUser(@RequestBody User user) { try{ return userService.insertUser(user); }catch (Exception e){ e.printStackTrace(); return userService.resultMap("error", e.getMessage(), "", "", "", ""); } } }
com/example/demo/service/UserService.java
package com.example.demo.service; import com.example.demo.entity.Permission; import com.example.demo.entity.Role; import com.example.demo.entity.User; import com.example.demo.mapper.PermissionMapper; import com.example.demo.mapper.UserMapper; import com.example.demo.mapper.RoleMapper; import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.util.stream.Collectors; /** * 服务层 */ @Service public class UserService { @Autowired private UserMapper userMapper; @Autowired private RoleMapper roleMapper; @Autowired private PermissionMapper permissionMapper; public User findUserByName(String userName) { User user = userMapper.findUserByName(userName); //用户角色的集合 List<Role> roleList = roleMapper.findRoleListByUserId(user.getId()); user.setRoleList(roleList); return user; } public Map<String, Object> findRoleListByUserId(int userId){ //用戶具有的角色集合 List<Role> beRoleList = roleMapper.findRoleListByUserIdNotPermission(userId); //用戶没有的角色集合 List<Role> notRoleList = roleMapper.findNotRoleListByUserIdNotPermission(userId); //所有角色集合 Collection<?> allRoleList = CollectionUtils.union(beRoleList, notRoleList); return this.resultMap("beRoleList", beRoleList, "notRoleList", notRoleList, "allRoleList", allRoleList); } @Transactional(rollbackFor = {Exception.class}) public Map<String, Object> updateUserRole(User user){ int removeUserRole = userMapper.removeUserRoleByUserId(user.getId()); int addUserRole = 0; if (user.getRoleList().size()!=0 && user.getRoleList()!=null){ addUserRole = userMapper.addUserRole(user); } return this.resultMap("removeUserRole", removeUserRole, "addUserRole", addUserRole, "", ""); } public Map<String, Object> findPermissionListByRoleId(int roleId){ //角色具有的权限集合 List<Permission> bePermissionList = permissionMapper.findByPermissionListByRoleId(roleId); //角色没有的权限集合 List<Permission> notPermissionList = permissionMapper.findNotPermissionListByRoleId(roleId); //所有权限集合 Collection<?> allPermissionList = CollectionUtils.union(bePermissionList, notPermissionList); return this.resultMap("bePermissionList", bePermissionList, "notPermissionList", notPermissionList, "allPermissionList", allPermissionList); } @Transactional(rollbackFor = {Exception.class}) public Map<String, Object> updateRolePermission(Role role){ int removeRolePermission = roleMapper.removeRolePermissionByRoleId(role.getId()); int addRolePermission = 0; if (role.getPermissionList().size()!=0 && role.getPermissionList()!=null){ addRolePermission = roleMapper.addRolePermission(role); } return this.resultMap("removeRolePermission", removeRolePermission, "addRolePermission", addRolePermission, "", ""); } @Transactional(rollbackFor = {Exception.class}) public Map<String, Object> removeRole(Role role){ int removeRolePermission = roleMapper.removeRolePermissionByRoleId(role.getId()); int removeRole = roleMapper.removeRoleByRoleId(role.getId()); int removeUserRole = userMapper.removeUserRoleByRoleId(role.getId()); return this.resultMap("removeRolePermission", removeRolePermission, "removeRole", removeRole, "removeUserRole", removeUserRole); } public Map<String, Object> queryUserListPage(User user){ //当前页页码 int pageNow = user.getReserve1() < 1 ? 1 : user.getReserve1(); //当前页第一行索引 user.setReserve1(5*(pageNow - 1)); List<User> userListPage = userMapper.queryUserListPage(user); int userRowCount = userMapper.getUserRowCount(user); return this.resultMap("userListPage", userListPage, "userRowCount", userRowCount, "", ""); } public Map<String, Object> addRole(Role role){ int addRole = roleMapper.addRole(role); return this.resultMap("addRole", addRole, "", "", "", ""); } @Transactional(rollbackFor = {Exception.class}) public Map<String, Object> removeUser(User user){ int removeUser = userMapper.removeUserByUserId(user.getId()); int removeUserRole = userMapper.removeUserRoleByUserId(user.getId()); return this.resultMap("removeUser", removeUser, "removeUserRole", removeUserRole, "", ""); } public Map<String, Object> insertUser(User user) { int addUser = userMapper.insertUser(user); return this.resultMap("addUser", addUser, "", "", "", ""); } public Map<String, Object> resultMap(String str1, Object obj1, String str2, Object obj2, String str3, Object obj3){ Map<String, Object> resultMap = new HashMap<>(); if (!"".equals(str1) || !"".equals(obj1)) resultMap.put(str1, obj1); if (!"".equals(str2) || !"".equals(obj2)) resultMap.put(str2, obj2); if (!"".equals(str3) || !"".equals(obj3)) resultMap.put(str3, obj3); return resultMap; } }
CDN模式下的vue、vue-router、template模板挂载操作
参考:src/main/resources/static/index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <!-- import CSS --> <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> </head> <body> <div id="app"> <router-view></router-view> </div> <template id="sign"> <div class="handle"> <div class="handle-input"> <div class="high70"> <span>用户名称:</span> <el-input v-model="username" placeholder="请输入用户名称" clearable></el-input> </div> <div class="high70"> <span>密码:</span> <el-input v-model="password" placeholder="请输入密码" clearable show-password></el-input> </div> <el-button @click="login" plain>登录</el-button> </div> </div> </template> <template id="manager"> <el-tabs :tab-position="tabPosition"> <el-tab-pane label="用户设置" v-if="permissionsFlag.query_user || permissionsFlag.add_user || permissionsFlag.allot_roles || permissionsFlag.remove_user"> <el-row type="flex" justify="center"> <el-col :span="18"> <div class="handle-area"> <div class="demo-input" v-show="permissionsFlag.query_user || permissionsFlag.add_user || permissionsFlag.allot_roles || permissionsFlag.remove_user"> <span>用户ID:</span> <el-input v-model="id" placeholder="请输入用户ID" type="number" size="medium" clearable></el-input> <span>用户名称:</span> <el-input v-model="username" placeholder="请输入用户名称" size="medium" clearable></el-input> <el-button :plain="true" @click="queryUserList" icon="el-icon-search" size="medium">查询用户</el-button> </div> <el-button v-show="permissionsFlag.add_user" :plain="true" @click="handleAddUser" icon="el-icon-user" size="medium">新建用户</el-button> </div> <el-table :data="tableData" border highlight-current-row> <el-table-column prop="id" label="用户ID"> </el-table-column> <el-table-column prop="username" label="用户名称"> </el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <el-button v-show="permissionsFlag.allot_roles" type="text" icon="el-icon-set-up" @click="handleEditUser(scope.$index, scope.row)">分配角色 </el-button> <el-button v-show="permissionsFlag.remove_user" type="text" icon="el-icon-remove-outline" @click="handleDeleteUser(scope.$index, scope.row)">删除用户 </el-button> </template> </el-table-column> </el-table> <el-pagination layout="total, prev, pager, next, jumper" :total="userRowCount" :page-size="5" :current-page.sync="currentPage" @current-change="queryUserListPage" :hide-on-single-page="hidePageFlag" background> </el-pagination> </el-col> </el-row> <el-dialog :title="roleTitle" :visible.sync="dialogEditUser" :before-close="handleClose"> <el-transfer v-model="transferValue" :data="transferData" :titles="['待分配角色', '已分配角色']"> </el-transfer> <span slot="footer" class="dialog-footer"> <el-button @click="handleClose">取 消</el-button> <el-button type="primary" @click="assignRoles">确 定</el-button> </span> </el-dialog> <el-dialog title="新建用户" :visible.sync="dialogAddUser" :before-close="handleClose"> <div class="high70"> <span>用户名称:</span> <el-input v-model="username" placeholder="请输入用户名称" size="medium" clearable></el-input> </div> <div class="high70"> <span>密码:</span> <el-input v-model="password" placeholder="请输入密码" size="medium" clearable show-password></el-input> </div> <div class="high70"> <span>再次输入密码:</span> <el-input v-model="password1" placeholder="请再次输入密码" size="medium" show-password></el-input> </div> <div class="high70"> <span>备注:</span> <el-input v-model="reserve" placeholder="请输入用户备注" size="medium"></el-input> </div> <span slot="footer" class="dialog-footer"> <el-button @click="handleClose">取 消</el-button> <el-button type="primary" @click="insertUser">确 定</el-button> </span> </el-dialog> </el-tab-pane> <el-tab-pane label="角色设置" v-if="permissionsFlag.allot_roles || permissionsFlag.query_role || permissionsFlag.add_role || permissionsFlag.remove_role || permissionsFlag.allot_permission"> <el-row type="flex" justify="center"> <el-col :span="18"> <div class="handle-area"> <el-button v-show="permissionsFlag.allot_roles || permissionsFlag.query_role || permissionsFlag.add_role || permissionsFlag.remove_role || permissionsFlag.allot_permission" :plain="true" @click="queryRoleList" icon="el-icon-search" size="medium">角色查询</el-button> <el-button v-show="permissionsFlag.add_role" :plain="true" @click="handleAddRole" icon="el-icon-circle-plus-outline" size="medium">新建角色</el-button> </div> <el-table :data="roleData" border highlight-current-row> <el-table-column prop="id" label="角色ID"> </el-table-column> <el-table-column prop="name" label="角色名称"> </el-table-column> <el-table-column prop="description" label="角色描述"> </el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <el-button v-show="permissionsFlag.allot_permission" type="text" icon="el-icon-s-operation" @click="handleEditRole(scope.$index, scope.row)">分配权限 </el-button> <el-button v-show="permissionsFlag.remove_role" type="text" icon="el-icon-remove-outline" @click="handleDeleteRole(scope.$index, scope.row)">删除角色 </el-button> </template> </el-table-column> </el-table> </el-col> </el-row> <el-dialog :title="permissionsTitle" :visible.sync="dialogEditRole" :before-close="handleClose"> <el-checkbox-group v-model="checkedPermissions" class="el-checkbox-group-dialog"> <el-checkbox v-for="permission in permissions" :label="permission.name" :key="permission.name">{{permission.description}}</el-checkbox> </el-checkbox-group> <span slot="footer" class="dialog-footer"> <el-button @click="handleClose">取 消</el-button> <el-button type="primary" @click="assignPermissions">确 定</el-button> </span> </el-dialog> <el-dialog title="新建角色" :visible.sync="dialogAddRole" :before-close="handleClose"> <div class="high70"> <span>角色名称:</span> <el-input v-model="name" placeholder="请输入角色名称" size="medium"></el-input> </div> <div class="high70"> <span>角色描述:</span> <el-input v-model="description" placeholder="请输入角色描述" size="medium"></el-input> </div> <span slot="footer" class="dialog-footer"> <el-button @click="handleClose">取 消</el-button> <el-button type="primary" @click="insertRole">确 定</el-button> </span> </el-dialog> </el-tab-pane> <el-tab-pane label="权限列表" v-if="permissionsFlag.allot_permission"> <el-row type="flex" justify="center"> <el-col :span="18"> <div class="handle-area"> <el-button v-show="permissionsFlag.allot_permission" :plain="true" @click="queryPermissionList" icon="el-icon-search" size="medium">权限查询</el-button> </div> <el-table :data="permissionData" border highlight-current-row> <el-table-column prop="id" label="权限ID"> </el-table-column> <el-table-column prop="name" label="权限名称"> </el-table-column> <el-table-column prop="description" label="权限描述"> </el-table-column> </el-table> </el-col> </el-row> </el-tab-pane> </el-tabs> </template> </body> <!-- import Vue before Element --> <script src="https://unpkg.com/vue@2/dist/vue.js"></script> <!-- import JavaScript --> <script src="https://unpkg.com/element-ui/lib/index.js"></script> <!-- import Axios --> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <!-- import Router --> <script src="https://cdn.staticfile.org/vue-router/2.7.0/vue-router.min.js"></script> <script> const Sign = { props: ['todo'], template: '#sign', data() { return { username: '', password: '', permissionsStr: [], token: '' } }, methods: { login() { axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; axios({ method: 'post', url: '/user/login', data: JSON.stringify({ username: this.username, password: this.password }) }).then(response => { if(typeof response.data.error !== 'undefined' || response.data.error != null){ this.username = ''; this.password = ''; ELEMENT.Notification.error({ title: '登录失败!', message: response.data.error, position: 'top-right', showClose: false, offset: 110 }); return; } this.permissionsStr = response.data.permissions; this.token = response.data.token; this.$router.push({ name: 'manager', params: { token: this.token, permissionsStr: this.permissionsStr } }); }).catch(error => { console.log(error); ELEMENT.Message(error); }); } }, mounted() { console.log('组件Sign被挂载了'); } }; const Manager = { // todo-item 组件现在接受一个 // "prop",类似于一个自定义 attribute。 // 这个 prop 名为 todo。 props: ['todo'], template: '#manager', data() { return { // 角色信息 roleData: [], roleId: null, name: '', description: '', dialogEditRole: false, dialogAddRole: false, permissions: [], checkedPermissions: [], permissionList: [], permissionsTitle: '', // 权限信息 permissionData: [], // 穿梭框 transferValue: [], transferData: [], // 表格及分页 tableData: [], tabPosition: 'top', userRowCount: 0, currentPage: 0, hidePageFlag: true, // 弹框 dialogEditUser: false,// 分配角色弹框 dialogAddUser: false,// 新建用户弹框 // 用户信息 allRoleList: [], roleList: [], id: null, username: '', password: '', password1: '', reserve: '', roleTitle: '', permissionsStr: [], permissionsFlag: { query_user: false, add_user: false, remove_user: false, allot_roles: false, query_role: false, add_role: false, remove_role: false, allot_permission: false }, token: '' } }, methods: { queryPermissionList() { axios.defaults.headers.token = this.token; axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; axios({ method: 'post', url: '/user/findPermissionListByRoleId', data: JSON.stringify({ id: 0 }) }).then(response => { if(typeof response.data.error !== 'undefined' || response.data.error != null){ ELEMENT.Notification.error({ title: '查询失败!', message: response.data.error, position: 'top-right', showClose: false, offset: 110 }); return; } this.permissionData = response.data.allPermissionList; }).catch(error => { console.log(error); ELEMENT.Message(error); }); }, queryRoleList() { axios.defaults.headers.token = this.token; axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; axios({ method: 'post', url: '/user/findRoleListByUserId', data: JSON.stringify({ id: 0 }) }).then(response => { if(typeof response.data.error !== 'undefined' || response.data.error != null){ ELEMENT.Notification.error({ title: '查询失败!', message: response.data.error, position: 'top-right', showClose: false, offset: 110 }); return; } this.roleData = response.data.allRoleList; }).catch(error => { console.log(error); ELEMENT.Message(error); }); }, handleEditRole(index, row){ axios.defaults.headers.token = this.token; axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; axios({ method: 'post', url: '/user/findPermissionListByRoleId', data: JSON.stringify({ id: row.id }) }).then(response => { console.log(response.data); if(typeof response.data.error !== 'undefined' || response.data.error != null){ ELEMENT.Notification.error({ title: '查询失败!', message: response.data.error, position: 'top-right', showClose: false, offset: 110 }); return; } this.permissions = response.data.allPermissionList; for(let item of response.data.bePermissionList){ this.checkedPermissions.push(item.name); } this.roleId = row.id; this.name = row.name; this.permissionsTitle = '为角色'+row.name+'分配权限'; this.dialogEditRole = true; }).catch(error => { console.log(error); ELEMENT.Message(error); }); }, handleDeleteRole(index, row){ ELEMENT.MessageBox.confirm('此操作将永久删除角色'+row.name+', 是否继续?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { axios.defaults.headers.token = this.token; axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; axios({ method: 'post', url: '/user/removeRole', data: JSON.stringify({ id: row.id }) }).then(response => { if(typeof response.data.error !== 'undefined' || response.data.error != null){ ELEMENT.Notification.error({ title: '角色'+row.name+',删除失败!', message: response.data.error, position: 'top-right', showClose: false, offset: 110 }); return; } if (response.data.removeRolePermission >= 0 && response.data.removeRole >= 0 && response.data.removeUserRole >= 0) { this.queryRoleList(); ELEMENT.Notification({ title: '删除成功', message: '角色'+row.name+',删除成功!', type: 'success', position: 'top-right', showClose: false, offset: 110 }); } }).catch(error => { ELEMENT.Message({ type: 'info', message: error}); }); }).catch(() => { ELEMENT.Message({ type: 'info', message: '已取消删除'}); }); }, handleAddRole(){ this.dialogAddRole = true; this.name = ''; this.description = ''; }, insertRole(){ axios.defaults.headers.token = this.token; axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; axios({ method: 'post', url: '/user/addRole', data: JSON.stringify({ name: this.name, description: this.description }) }).then(response => { if(typeof response.data.error !== 'undefined' || response.data.error != null){ ELEMENT.Notification.error({ title: '角色'+this.name+',添加失败!', message: response.data.error, position: 'top-right', showClose: false, offset: 110 }); this.dialogAddRole = false; this.name = ''; this.description = ''; return; } if(response.data.addRole > 0) { this.queryRoleList(); ELEMENT.Notification({ title: '添加成功', message: '角色'+this.name+',添加成功!', type: 'success', position: 'top-right', showClose: false, offset: 110 }); this.dialogAddRole = false; this.name = ''; this.description = ''; } console.log(response.data); }).catch(error => { ELEMENT.Message({ type: 'info', message: error}); }); }, assignPermissions(){ for(let item of this.permissions){ for(let i in this.checkedPermissions){ if (this.checkedPermissions[i] == item.name) { this.permissionList.push(item); } } } axios.defaults.headers.token = this.token; axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; axios({ method: 'post', url: '/user/updateRolePermission', data: JSON.stringify({ id: this.roleId, permissionList: this.permissionList, }) }).then(response => { if(typeof response.data.error !== 'undefined' || response.data.error != null){ this.dialogEditRole = false; ELEMENT.Notification.error({ title: '为角色'+this.name+',分配权限失败!', message: response.data.error, position: 'top-right', showClose: false, offset: 110 }); this.roleId = null; this.name = null; this.permissions = []; this.checkedPermissions = []; this.permissionList = []; this.permissionsTitle = ''; return; } if (response.data.removeRolePermission >= 0 && response.data.addRolePermission >= 0) { this.dialogEditRole = false; ELEMENT.Notification({ title: '更新成功', message: '为角色'+this.name+',分配权限成功!', type: 'success', position: 'top-right', showClose: false, offset: 110 }); this.roleId = null; this.name = null; this.permissions = []; this.checkedPermissions = []; this.permissionList = []; this.permissionsTitle = ''; } console.log(response.data); }).catch(error => { console.log(error); ELEMENT.Message(error); }); }, // ------------------------用户设置部分-------------- queryUserList() { axios.defaults.headers.token = this.token; axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; axios({ method: 'post', url: '/user/queryUserListPage', data: JSON.stringify({ id: this.id, username: this.username }) }).then(response => { if(typeof response.data.error !== 'undefined' || response.data.error != null){ ELEMENT.Notification.error({ title: '查询用户失败!', message: response.data.error, position: 'top-right', showClose: false, offset: 110 }); return; } console.log(response.data); this.tableData = response.data.userListPage; this.userRowCount = response.data.userRowCount; if(this.userRowCount > 5) { this.hidePageFlag = false; } else { this.hidePageFlag = true; } this.currentPage = 1; }).catch(error => { console.log(error); ELEMENT.Message(error); }); }, queryUserListPage() { axios.defaults.headers.token = this.token; axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; axios({ method: 'post', url: '/user/queryUserListPage', data: JSON.stringify({ id: this.id, username: this.username, reserve1: this.currentPage }) }).then(response => { if(typeof response.data.error !== 'undefined' || response.data.error != null){ ELEMENT.Notification.error({ title: '查询用户失败!', message: response.data.error, position: 'top-right', showClose: false, offset: 110 }); return; } console.log(response.data); this.tableData = response.data.userListPage; this.userRowCount = response.data.userRowCount; if(this.userRowCount > 5) { this.hidePageFlag = false; } else { this.hidePageFlag = true; } }).catch(error => { console.log(error); ELEMENT.Message(error); }); }, handleEditUser(index, row) { axios.defaults.headers.token = this.token; axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; axios({ method: 'post', url: '/user/findRoleListByUserId', data: JSON.stringify({ id: row.id }) }).then(response => { if(typeof response.data.error !== 'undefined' || response.data.error != null){ this.dialogEditUser = false; ELEMENT.Notification.error({ title: '为用户'+row.username+'分配角色失败!', message: response.data.error, position: 'top-right', showClose: false, offset: 110 }); return; } console.log(response.data); for(let item of response.data.allRoleList){ this.transferData.push({ key: item.id, label: item.description }); } for(let item of response.data.beRoleList){ this.transferValue.push(item.id); } this.id = row.id; this.username = row.username; this.allRoleList = response.data.allRoleList; this.roleTitle = '为用户'+row.username+'分配角色'; this.dialogEditUser = true; }).catch(error => { console.log(error); ELEMENT.Message(error); }); }, assignRoles() { for(let item of this.allRoleList){ for(let i in this.transferValue){ if (this.transferValue[i] == item.id) { this.roleList.push(item); } } } axios.defaults.headers.token = this.token; axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; axios({ method: 'post', url: '/user/updateUserRole', data: JSON.stringify({ id: this.id, username: this.username, roleList: this.roleList }) }).then(response => { if(typeof response.data.error !== 'undefined' || response.data.error != null){ this.dialogEditUser = false; ELEMENT.Notification.error({ title: '为用户'+row.username+'分配角色失败!', message: response.data.error, position: 'top-right', showClose: false, offset: 110 }); this.id = null; this.username = ''; this.allRoleList = []; this.roleList = []; this.transferData = []; this.transferValue = []; return; } if (response.data.removeUserRole >= 0 && response.data.addUserRole >= 0) { this.dialogEditUser = false; ELEMENT.Notification({ title: '更新成功', message: '为用户'+this.username+',分配角色成功!', type: 'success', position: 'top-right', showClose: false, offset: 110 }); this.id = null; this.username = ''; this.allRoleList = []; this.roleList = []; this.transferData = []; this.transferValue = []; } console.log(response.data); }).catch(error => { console.log(error); }); }, handleDeleteUser(index, row) { //this.ruleForm = Object.assign({}, row, index); //这句是关键!!! ELEMENT.MessageBox.confirm('此操作将永久删除用户'+row.id+', 是否继续?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { axios.defaults.headers.token = this.token; axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; axios({ method: 'post', url: '/user/removeUser', data: JSON.stringify({ id: row.id }) }).then(response => { if(typeof response.data.error !== 'undefined' || response.data.error != null){ ELEMENT.Notification.error({ title: '用户'+row.username+',删除失败!', message: response.data.error, position: 'top-right', showClose: false, offset: 110 }); return; } if (response.data.removeUser >= 0 && response.data.removeUserRole >= 0) { if(((this.userRowCount - 1) % 5 == 0) && ((this.userRowCount - 1) / 5 == (this.currentPage - 1))) { this.currentPage = this.currentPage - 1; } this.queryUserListPage(); ELEMENT.Notification({ title: '删除成功', message: '用户'+row.username+',删除成功!', type: 'success', position: 'top-right', showClose: false, offset: 110 }); } }).catch(error => { ELEMENT.Message({ type: 'info', message: error}); }); }).catch(() => { ELEMENT.Message({ type: 'info', message: '已取消删除'}); }); }, handleClose() { // 用户设置 this.dialogEditUser = false; this.dialogAddUser = false; this.id = null; this.username = ''; this.password = ''; this.password1 = ''; this.reserve = ''; this.allRoleList = []; this.roleList = []; this.transferData = []; this.transferValue = []; this.roleTitle = ''; // 角色设置 this.dialogEditRole = false; this.dialogAddRole = false; this.roleId = null; this.name = null; this.permissions = []; this.checkedPermissions = []; this.permissionList = []; this.permissionsTitle = ''; ELEMENT.Message({ message: '取消操作,系统不会保留任何更改', type:'warning' }); }, handleAddUser(){ this.dialogAddUser = true; this.id = null; this.username = ''; this.password = ''; this.password1 = ''; this.reserve = ''; }, insertUser() { axios.defaults.headers.token = this.token; axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; axios({ method: 'post', url: '/user/insertUser', data: JSON.stringify({ username: this.username, password: this.password, reserve: this.reserve }) }).then(response => { if(response.data.addUser > 0) { this.dialogAddUser = false; this.password = ''; this.password1 = ''; this.reserve = ''; this.queryUserList(); ELEMENT.Notification({ title: '添加成功', message: '用户'+this.username+',添加成功!', type: 'success', position: 'top-right', showClose: false, offset: 110 }); } if(typeof response.data.error !== 'undefined' || response.data.error != null){ this.dialogAddUser = false; this.password = ''; this.password1 = ''; this.reserve = ''; this.queryUserList(); ELEMENT.Notification.error({ title: '用户'+this.username+',添加失败!', message: response.data.error, position: 'top-right', showClose: false, offset: 110 }); return; } console.log(response.data); }).catch(error => { ELEMENT.Message({ type: 'info', message: error}); }); } }, mounted() { console.log('组件Manager被挂载了'); }, created() { this.permissionsStr = this.$route.params.permissionsStr; this.token = this.$route.params.token; for(let key in this.permissionsFlag){ for(let i in this.permissionsStr){ if(this.permissionsStr[i] == key){ this.permissionsFlag[key] = true; } } console.log(key + '---' + this.permissionsFlag[key]) } } }; const router = new VueRouter({ routes:[ { path: '/', name: 'sign', component: Sign }, { path: '/manager', name: 'manager', component: Manager } ] }); new Vue({ router, el: '#app', data: {}, methods: {} }) </script> <style> .el-pagination { display: flex; justify-content: flex-end; align-items: center; height: 60px; } .el-transfer { display: flex; justify-content: center; align-items: center; } .handle-area { display: flex; align-items: center; justify-content: space-between; height: 70px; flex-wrap: nowrap; } .demo-input { display: flex; align-items: center; } .demo-input span { white-space: nowrap; } .el-dialog__body { display: flex; flex-direction: column; justify-content: center; flex-wrap: nowrap; height: 280px; } .el-checkbox-group-dialog { height: 100%; display: flex; flex-direction: column; justify-content: center; margin: auto; } .el-checkbox { height: 30px; line-height: 30px; } .high70 { height: 70px; } .handle { position: absolute; width: 100%; height: 100%; top: 0; left: 0; margin: 0; background-image: url(banner.jpg); background-position: center; background-repeat: no-repeat; background-size: cover; overflow: hidden; display: flex; justify-content: center; align-items: center; } .handle-input { display: flex; flex-direction: column; } </style> </html>
具体看index.html 代码,如果没看懂,参考网址:
https://blog.csdn.net/laow1314/article/details/109323527
https://blog.csdn.net/qq_29722281/article/details/85016524
权限相关表
最基础的CRUD操作无法体现shiro的功能,需引入RBAC (Role-Based Access Control)思想,配置至少需要三张主表分别代表用户(user)、角色(role)、权限(permission),及两个附表分别是用户和角色(user_role),角色与权限(role_permission)表,表结构如下:
对应的实体如下:
1、com/example/demo/entity/User.java
package com.example.demo.entity; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.*; import java.io.IOException; import java.util.ArrayList; import java.util.List; @Data @ToString @Builder @AllArgsConstructor @NoArgsConstructor public class User { /** * 主键 */ private int id; /** * 用户名 */ private String username; /** * 密码 */ private String password; /** * 保留字段 */ private String reserve; /** * 保留字段1 */ private int reserve1; /** * 角色集合 */ private List<Role> roleList = new ArrayList<>(); public User(String json) throws IOException { User user = new ObjectMapper().readValue(json,User.class); this.id = user.getId(); this.username = user.getUsername(); this.password = user.getPassword(); this.reserve = user.getReserve(); this.reserve1 = user.getReserve1(); this.roleList = user.getRoleList(); } }
2、com/example/demo/entity/Role.java
package com.example.demo.entity; import lombok.*; import java.util.ArrayList; import java.util.List; @Data @ToString @Builder @AllArgsConstructor @NoArgsConstructor public class Role { /** * 主键 */ private int id; /** * 角色名称 */ private String name; /** * 角色描述 */ private String description; /** * 权限集合 */ private List<Permission> permissionList=new ArrayList<>(); }
3、com/example/demo/entity/Permission.java
package com.example.demo.entity; import lombok.*; @Data @ToString @Builder @AllArgsConstructor @NoArgsConstructor public class Permission { /** * 主键 */ private int id; /** * 权限名称 */ private String name; /** * 权限描述 */ private String description; }
前后端分离项目中使用shiro的session鉴别用户身份
1、src/main/resources/static/index.html
<template id="sign"> <div class="handle"> <div class="handle-input"> <div class="high70"> <span>用户名称:</span> <el-input v-model="username" placeholder="请输入用户名称" clearable></el-input> </div> <div class="high70"> <span>密码:</span> <el-input v-model="password" placeholder="请输入密码" clearable show-password></el-input> </div> <el-button @click="login" plain>登录</el-button> </div> </div> </template>
login() { axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; axios({ method: 'post', url: '/user/login', data: JSON.stringify({ username: this.username, password: this.password }) }).then(response => { if(typeof response.data.error !== 'undefined' || response.data.error != null){ this.username = ''; this.password = ''; ELEMENT.Notification.error({ title: '登录失败!', message: response.data.error, position: 'top-right', showClose: false, offset: 110 }); return; } this.permissionsStr = response.data.permissions; this.token = response.data.token; this.$router.push({ name: 'manager', params: { token: this.token, permissionsStr: this.permissionsStr } }); }).catch(error => { console.log(error); ELEMENT.Message(error); }); }
2、com/example/demo/controller/UserCtrl.java
/** * 登录接口 * * @param user user * @return resultMap */ @RequestMapping(value = "/login", method = RequestMethod.POST) public Map<String, Object> login(@RequestBody User user) { //拿到主体 Subject subject = SecurityUtils.getSubject(); try { UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUsername(), user.getPassword()); subject.login(usernamePasswordToken); Object permissions = subject.getSession().getAttribute("permissions"); subject.getSession().removeAttribute("permissions"); return userService.resultMap("permissions", permissions, "token", subject.getSession().getId(), "", ""); }catch (Exception e){ e.printStackTrace(); return userService.resultMap("error", e.getMessage(), "", "", "", ""); } }
3、com/example/demo/config/UserRealm.java
/** * 认证 * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //从token中获取用户信息 String uesrName = (String) authenticationToken.getPrincipal(); User user = userService.findUserByName(uesrName); if (user == null) { return null; } //权限集合 Set<String> stringPermissionSet = new HashSet<>(); user.getRoleList().forEach(role -> { stringPermissionSet.addAll(role.getPermissionList().stream().map(Permission::getName).collect(Collectors.toList())); }); SecurityUtils.getSubject().getSession().setAttribute("permissions", stringPermissionSet); //密码 String pwd = user.getPassword(); if (pwd == null || "".equals(pwd)) { return null; } return new SimpleAuthenticationInfo(uesrName, pwd, this.getClass().getName()); }
4、com/example/demo/service/UserService.java
public User findUserByName(String userName) { User user = userMapper.findUserByName(userName); //用户角色的集合 List<Role> roleList = roleMapper.findRoleListByUserId(user.getId()); user.setRoleList(roleList); return user; }
5、src/main/resources/mapper/UserMapper.xml
<resultMap id="resultUser" type="com.example.demo.entity.User"> <result property="id" column="id" /> <result property="username" column="username" /> <result property="password" column="password" /> </resultMap> <select id="findUserByName" resultMap="resultUser" parameterType="String"> select id, username, password from user where username = #{userName} </select>
6、src/main/resources/mapper/RoleMapper.xml
<resultMap id="roleResult" type="com.example.demo.entity.Role"> <result property="id" column="id" /> <result property="name" column="name" /> <result property="description" column="description" /> <collection property="permissionList" column="id" select="com.example.demo.mapper.PermissionMapper.findByPermissionListByRoleId" /> </resultMap> <resultMap id="roleResultNotPermission" type="com.example.demo.entity.Role"> <result property="id" column="id" /> <result property="name" column="name" /> <result property="description" column="description" /> </resultMap> <select id="findRoleListByUserId" resultMap="roleResult" parameterType="int"> select r.id id, r.name name, r.description description from user_role ur left join role r on ur.role_id = r.id where ur.user_id = #{userId} </select>
7、src/main/resources/mapper/PermissionMapper.xml
<resultMap id="permissionResult" type="com.example.demo.entity.Permission"> <result property="id" column="id" /> <result property="name" column="name" /> <result property="description" column="description" /> </resultMap> <select id="findByPermissionListByRoleId" resultMap="permissionResult" parameterType="int"> select p.id id, p.name name, p.description description from role_permission rp left join permission p on rp.permission_id = p.id where rp.role_id = #{roleId} </select>
8、页面
9、src/main/resources/static/index.html
queryPermissionList() { axios.defaults.headers.token = this.token; axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; axios({ method: 'post', url: '/user/findPermissionListByRoleId', data: JSON.stringify({ id: 0 }) }).then(response => { if(typeof response.data.error !== 'undefined' || response.data.error != null){ ELEMENT.Notification.error({ title: '查询失败!', message: response.data.error, position: 'top-right', showClose: false, offset: 110 }); return; } this.permissionData = response.data.allPermissionList; }).catch(error => { console.log(error); ELEMENT.Message(error); }); },
10、com/example/demo/config/UserSessionManager.java
package com.example.demo.config; import org.apache.shiro.web.servlet.ShiroHttpServletRequest; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.apache.shiro.web.util.WebUtils; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.Serializable; public class UserSessionManager extends DefaultWebSessionManager { public static final String AUTHORIZATION="token"; public UserSessionManager() { super(); } @Override protected Serializable getSessionId(ServletRequest request, ServletResponse response) { //获取sessionId String sessionId= WebUtils.toHttp(request).getHeader(AUTHORIZATION); if (sessionId!=null && sessionId!=""){ request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); return sessionId; }else { return super.getSessionId(request,response); } } }
11、参考链接:
https://blog.csdn.net/qq_25046827/article/details/124540457
https://blog.csdn.net/palerock/article/details/73457415
前端控制按钮显隐,后端请求路径拦截配置
用户登陆成功后会返回用户具有的权限列表,根据权限列表控制前端控制按钮显隐及后端请求拦截路径,
总结如下:
相关代码
com/example/demo/config/ShiroConfig.java
package com.example.demo.config; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.mgt.SessionManager; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { System.out.println("ShiroConfig ShiroFilterFactoryBean 执行"); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //设置SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //如果访问需要登录的某个接口,却没有登录,则调用此接口(如果不是前后端分离,则跳转页面) //shiroFilterFactoryBean.setLoginUrl("/index.html"); //登录成功后,跳转的链接,若前后端分离,没必要设置这个 //shiroFilterFactoryBean.setSuccessUrl(""); //登录成功,未授权会调用此方法 //shiroFilterFactoryBean.setUnauthorizedUrl("/user/*"); //拦截路径,必须使用:LinkedHashMap,要不然拦截效果会时有时无,因为使用的是无序的Map Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); //key=正则表达式路径,value=org.apache.shiro.web.filter.mgt.DefaultFilter //退出过滤器 //filterChainDefinitionMap.put("/logout", "logout"); //匿名可以访问,游客模式 filterChainDefinitionMap.put("/", "anon"); filterChainDefinitionMap.put("/user/login", "anon"); //登录用户才可以访问 filterChainDefinitionMap.put("/user/**", "authc"); //管理员角色才能访问 //filterChainDefinitionMap.put("/user/**", "roles[admin]"); //有权限才能访问 filterChainDefinitionMap.put("/user/queryUserListPage", "perms[query_user, add_user, allot_roles, remove_user]"); filterChainDefinitionMap.put("/user/insertUser", "perms[add_user]"); filterChainDefinitionMap.put("/user/removeUser", "perms[remove_user]"); filterChainDefinitionMap.put("/user/findRoleListByUserId", "perms[allot_roles, query_role, add_role, remove_role, allot_permission]"); filterChainDefinitionMap.put("/user/updateUserRole", "perms[allot_roles]"); filterChainDefinitionMap.put("/user/addRole", "perms[add_role]"); filterChainDefinitionMap.put("/user/removeRole", "perms[remove_role]"); filterChainDefinitionMap.put("/user/findPermissionListByRoleId", "perms[allot_permission]"); filterChainDefinitionMap.put("/user/updateRolePermission", "perms[allot_permission]"); //authc:url必须通过认证才可以访问 //anon:url可以匿名访问 //过滤链是顺序执行,从上而下,一般把/**,放到最下面 filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //如果不是前后端分离,不用设置setSessionManager securityManager.setSessionManager(sessionManager()); securityManager.setRealm(userRealm()); return securityManager; } /** * 自定义realm * * @return */ @Bean public UserRealm userRealm() { UserRealm userRealm = new UserRealm(); //因为数据库密码存的是明文,所以无需使用双重md5校验 // customRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return userRealm; } /** * 密码验证器,双重md5 * * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); //设置散列算法,使用md5算法 hashedCredentialsMatcher.setHashAlgorithmName("md5"); //散列次数,使用2次md5算法,相当于md5(md5(xxx)) hashedCredentialsMatcher.setHashIterations(2); return hashedCredentialsMatcher; } /** * 自定义SessionManager * * @return */ @Bean public SessionManager sessionManager() { UserSessionManager userSessionManager = new UserSessionManager(); //超时时间,默认 30分钟,会话超时,单位毫秒 // customSessionManager.setGlobalSessionTimeout(200000); return userSessionManager; } }
待完成
1、密码加解密;
2、记住我;