Catalogue of series articles
SpringBoot integrates SpringSecurity to realize permission control (I): implementation principle
SpringBoot integrates spring security to realize permission control (II): Design of basic model of permission data
SpringBoot integrates SpringSecurity to achieve permission control (3): front end dynamic loading routing and menu
SpringBoot integrates SpringSecurity to achieve permission control (IV): role management
The user of the system is called a user, and can only operate the system within the scope of authority granted.
Administrator is a special user with the highest authority of system operation.
- This paper will realize the management function of users, focusing on the role allocation of users.
– for new users (self registration), please refer to the following articles: Teach you how to verify email registration code through SpringBoot
- The results are as follows:
2.1 create user entity class
- This entity class corresponds to the user table and records the user's basic information.
/** * User table * * @author zhuhuix * @date 2020-04-03 */ @ApiModel(value = "User information") @Data @SuperBuilder @NoArgsConstructor @AllArgsConstructor @TableName("sys_user") public class SysUser implements Serializable { @TableId(value = "id", type = IdType.AUTO) private Long id; private String userName; @JsonIgnore private String password; private String nickName; /** * Gender 0 - unknown, 1-male,2-female */ private Integer gender; /** * Avatar address */ private String avatarUrl; private String country; private String province; private String city; @Email private String email; private String phone; private String remarks; @TableLogic private Boolean enabled; private Timestamp lastPasswordResetTime; private Timestamp createTime; @Builder.Default private Timestamp updateTime = Timestamp.valueOf(LocalDateTime.now()); }
2.2 create user role entity class
- The entity class corresponds to the user role table and records the user's corresponding role information (one corner user can correspond to multiple roles).
/** * User role table * * @author zhuhuix * @date 2021-09-29 */ @ApiModel(value = "User role information") @Data @SuperBuilder @NoArgsConstructor @AllArgsConstructor @TableName("sys_user_role") public class SysUserRole { @TableId(value = "id", type = IdType.AUTO) private Long id; private Long userId; private Long roleId; private Timestamp createTime; }
2.3 add user and user role Mapper interface
- User entities and user role entities can be operated through the Mapper interface
/** * User DAO interface * * @author zhuhuix * @date 2021-07-19 */ @Mapper public interface SysUserMapper extends BaseMapper<SysUser> { /** * Query user roles * @param userId User id * @return Role information */ @Select("select r.id,r.role_code,r.role_name,r.description,r.enabled,r.create_time,r.update_time " + "from sys_role r " + "INNER JOIN sys_user_role ur ON r.id=ur.role_id where ur.user_id=# ") List<SysRole> selectUserRoles(Long userId); /** * Query user permissions * @param userId User id * @return permissions information */ @Select("SELECT m.id, m.`path`, m.`name`, m.`component`, m.`icon`, m.`cache`, m.`hidden`, m.`redirect`, m.p_id " + "FROM " + "sys_menu m " + "INNER JOIN sys_permission p ON p.menu_id = m.id " + "INNER JOIN sys_user_role ur ON ur.role_id = p.role_id " + "INNER JOIN sys_user u ON u.id = ur.user_id " + "INNER JOIN sys_role r ON r.id = ur.role_id where ur.user_id=#"+ " and m.enabled=1 " + " order by m.sort " ) List<PermissionDto> selectUserPermission(Long userId); }
/** * User role DAO interface * * @author zhuhuix * @date 2021-09-29 */ @Mapper public interface SysUserRoleMapper extends BaseMapper<SysUserRole> { }
2.4 add, delete, modify and query services for users
/** * User management service interface * * @author zhuhuix * @date 2020-04-03 */ public interface SysUserService { /** * Add user * * @param user Users to be added * @return Add successful users */ SysUser create(SysUser user); /** * delete user * * @param user Users to be deleted * @return Delete successful users */ SysUser delete(SysUser user); /** * Modify user * * @param user User to be modified * @return User successfully modified */ SysUser update(SysUser user); /** * Find users by id * * @param id User id * @return User information */ SysUser findById(Long id); /** * Find users by userName * * @param userName User account * @return User corresponding to user account */ SysUser findByUserName(String userName); /** * Determine whether the registered mailbox exists * * @param email Mailbox number * @return Found */ boolean registerEmailExist(String email); /** * Get user information * * @return User information */ UserDetails getUserInfo(); /** * Modify user Avatar * * @param file file * @return json */ Map<String, String> updateAvatar(MultipartFile file); /** * Get user role information * * @param userId User id * @return Role information */ List<SysRole> getUserRoles(Long userId); /** * Save user role * * @param userId User id * @param roleIds Role id list * @return Is it successful */ Boolean saveUserRoles(Long userId,Set<Long> roleIds); /** * Get user permission information * * @param userId User id * @return permissions information */ List<PermissionDto> getUserPermission(Long userId); /** * Query user information according to criteria * * @param sysUserQueryDto query criteria * @return User list */ List<SysUser> list(SysUserQueryDto sysUserQueryDto); /** * Batch delete user * * @param ids List of user IDs to be deleted * @return Is it successful */ Boolean delete(Set<Long> ids); }
/** * User management service implementation class * * @author zhuhuix * @date 2020-04-03 */ @Slf4j @Service @RequiredArgsConstructor @Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class) public class SysUserServiceImpl implements SysUserService { private final SysUserMapper sysUserMapper; private final SysUserRoleMapper sysUserRoleMapper; private final UploadFileTool uploadFileTool; @Override @Transactional(rollbackFor = Exception.class) public SysUser create(SysUser user) { if (sysUserMapper.insert(user) > 0) { return user; } throw new RuntimeException("Failed to add user information"); } @Override @Transactional(rollbackFor = Exception.class) public SysUser delete(SysUser user) { QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>(); queryWrapper.lambda().eq(SysUser::getUserName, user.getUserName()); if (sysUserMapper.delete(queryWrapper) > 0) { return user; } throw new RuntimeException("Failed to delete user information"); } @Override @Transactional(rollbackFor = Exception.class) public SysUser update(SysUser user) { if (sysUserMapper.updateById(user) > 0) { return user; } throw new RuntimeException("Failed to update user information"); } @Override public SysUser findById(Long id) { return sysUserMapper.selectById(id); } @Override public SysUser findByUserName(String userName) { return sysUserMapper.selectOne(new QueryWrapper<SysUser>().lambda().eq(SysUser::getUserName, userName)); } @Override public boolean registerEmailExist(String email) { QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>(); queryWrapper.lambda().eq(SysUser::getEmail, email); return sysUserMapper.selectOne(queryWrapper) != null; } @Override public UserDetails getUserInfo() { UserDetailsService userDetailsService = SpringContextHolder.getBean(UserDetailsService.class); return userDetailsService.loadUserByUsername(getCurrentLoginUserName()); } @Override @Transactional(rollbackFor = Exception.class) public Map<String, String> updateAvatar(MultipartFile file) { SysUser sysUser = findByUserName(getCurrentLoginUserName()); UploadFile uploadFile = uploadFileTool.upload(sysUser.getUserName(), file.getOriginalFilename(), file); sysUser.setAvatarUrl(uploadFile.getType() + File.separator + uploadFile.getFileName()); update(sysUser); return new HashMap<String, String>(1) {{ put("avatar", uploadFile.getFileName()); }}; } @Override public List<PermissionDto> getUserPermission(Long userId) { return sysUserMapper.selectUserPermission(userId); } @Override public List<SysRole> getUserRoles(Long userId) { return sysUserMapper.selectUserRoles(userId); } @Override @Transactional(rollbackFor = Exception.class) public Boolean saveUserRoles(Long userId, Set<Long> roleIds) { // First, clear the user's original role information QueryWrapper<SysUserRole> queryWrapper = new QueryWrapper<>(); queryWrapper.lambda().eq(SysUserRole::getUserId, userId); sysUserRoleMapper.delete(queryWrapper); // Add again for (Long roleId : roleIds) { SysUserRole sysUserRole = new SysUserRole(); sysUserRole.setUserId(userId); sysUserRole.setRoleId(roleId); sysUserRole.setCreateTime(Timestamp.valueOf(LocalDateTime.now())); sysUserRoleMapper.insert(sysUserRole); } return true; } private String getCurrentLoginUserName() { final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null) { throw new RuntimeException("The login status has expired"); } if (authentication.getPrincipal() instanceof UserDetails) { UserDetails userDetails = (UserDetails) authentication.getPrincipal(); return (userDetails.getUsername()); } throw new RuntimeException("The information for the current login cannot be found"); } @Override public List<SysUser> list(SysUserQueryDto sysUserQueryDto) { QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>(); if (!StringUtils.isEmpty(sysUserQueryDto.getUserName())) { queryWrapper.lambda().like(SysUser::getUserName, sysUserQueryDto.getUserName()) .or().like(SysUser::getNickName, sysUserQueryDto.getUserName()); } if (!StringUtils.isEmpty(sysUserQueryDto.getCreateTimeStart()) && !StringUtils.isEmpty(sysUserQueryDto.getCreateTimeEnd())) { queryWrapper.and(wrapper -> wrapper.lambda().between(SysUser::getCreateTime, new Timestamp(sysUserQueryDto.getCreateTimeStart()), new Timestamp(sysUserQueryDto.getCreateTimeEnd()))); } return sysUserMapper.selectList(queryWrapper); } @Override @Transactional(rollbackFor = Exception.class) public Boolean delete(Set<Long> ids) { if (sysUserMapper.deleteBatchIds(ids) > 0) { return true; } throw new RuntimeException("Failed to delete user information"); } }
2.5 writing Controller layer
- The following 9 background interfaces are implemented:
/** * api User information * * @author zhuhuix * @date 2021-08-16 */ @Slf4j @RestController @AllArgsConstructor @RequestMapping("/api/user") @Api(tags = "User information interface") public class SysUserController { private final SysUserService sysUserService; @ApiOperation("Get current login user information") @GetMapping() public ResponseEntity<Object> getUserInfo() { return ResponseEntity.ok(sysUserService.getUserInfo()); } @ApiOperation("according to id Get user information") @GetMapping("/") public ResponseEntity<Object> getUserInfo(@PathVariable Long id) { return ResponseEntity.ok(sysUserService.findById(id)); } @ApiOperation("Update user information") @PostMapping() public ResponseEntity<Object> saveUser(@RequestBody SysUser user) { return ResponseEntity.ok(sysUserService.update(user)); } @PreAuthorize("hasAuthority('user:updateAvatar')") @ApiOperation("Modify user Avatar") @PostMapping(value = "/updateAvatar") public ResponseEntity<Object> updateAvatar(@RequestParam MultipartFile avatar) { return ResponseEntity.ok(sysUserService.updateAvatar(avatar)); } @ApiOperation("Query user list by criteria") @PostMapping("/list") public ResponseEntity<Object> getSysUserList(@RequestBody SysUserQueryDto sysUserQueryDto) { return ResponseEntity.ok(sysUserService.list(sysUserQueryDto)); } @ApiOperation("Batch delete user") @DeleteMapping public ResponseEntity<Object> deleteUsers(@RequestBody Set<Long> ids) { return ResponseEntity.ok(sysUserService.delete(ids)); } @ApiOperation("Get user role") @GetMapping("/roles/") public ResponseEntity<Object> getUserRoles(@PathVariable Long userId) { return ResponseEntity.ok(sysUserService.getUserRoles(userId)); } @ApiOperation("Save user role") @PostMapping("/roles/") public ResponseEntity<Object> saveUserRoles(@PathVariable Long userId, @RequestBody Set<Long> ids) { return ResponseEntity.ok(sysUserService.saveUserRoles(userId, ids)); } @ApiOperation("Get user permissions") @GetMapping("/permission/") public ResponseEntity<Object> getUserPermission(@PathVariable Long userId) { return ResponseEntity.ok(sysUserService.getUserPermission(userId)); } }3, Front end implementation
3.1 add user api access interface
import request from '@/utils/request' // Sign in export function login(data) { return request({ url: '/api/auth/login', method: 'post', data }) } // cancellation export function logout() { return request({ url: '/api/auth/logout', method: 'delete' }) } // Get current login user information export function getInfo() { return request({ url: '/api/user', method: 'get' }) } // Obtain user information according to user id export function getInfoById(id) { return request({ url: '/api/user/' + id, method: 'get' }) } // Save and update users export function saveUser(data) { return request({ url: '/api/user', method: 'post', data }) } // Batch delete users according to user id list export function deleteUser(ids) { return request({ url: '/api/user', method: 'delete', data: ids }) } // Get user list according to condition query export function getUserList(params) { return request({ url: '/api/user/list', method: 'post', data: JSON.stringify(params) }) } // Obtain user permissions according to user id export function getUserPermission(userId) { return request({ url: '/api/user/permission/' + userId, method: 'get' }) } // Get user role export function getUserRoles(userId) { return request({ url: '/api/user/roles/' + userId, method: 'get' }) } // Assign user roles export function saveUserRoles(userId, roleIds) { return request({ url: '/api/user/roles/' + userId, method: 'post', data: roleIds }) }
3.2 compiling front page
- We need to write a complete page:
-
Query users by login account or user name, registration start and end time, and display them in the form of list.
-
Click the "assign role" button to pop up the form and select role information to assign roles to users.
-
Select a user from the list (multiple choices are allowed), and click "delete" to delete the selected user. There should be a prompt before deleting.
- The key source codes of the front end are as follows:
– src/user/index.vue
<template> <div> <!--toolbar--> <div> <!-- search --> <el-input v-model="userName" size="small" clearable placeholder="Enter account or user name to search" style="width: 200px" @keyup.enter.native="doQuery" /> <el-date-picker v-model="createTime" :default-time="['00:00:00', '23:59:59']" type="daterange" range-separator=":" size="small" value-format="yyyy-MM-dd HH:mm:ss" start-placeholder="Start date" end-placeholder="End date" /> <el-button size="mini" type="success" icon="el-icon-search" @click="doQuery" >search</el-button> <el-button size="mini" type="danger" icon="el-icon-circle-plus-outline" :disabled="selections.length===0" @click="doDelete" >delete</el-button> </div> <el-row> <!--Role assignment form--> <el-dialog append-to-body :close-on-click-modal="false" :visible.sync="showDialog" title="Role assignment" width="600px"> <el-form ref="form" :inline="true" :model="form" size="small" label-width="76px"> <el-form-item label="Login account" prop="userName"> <el-input v-model="form.userName" :disabled="true" /> </el-form-item> <el-form-item label="nickname" prop="nickName"> <el-input v-model="form.nickName" :disabled="true" /> </el-form-item> <el-form-item style="margin-bottom: 0;" label="role" prop="userRoles"> <el-select v-model="userRoles" style="width: 455px" multiple filterable placeholder="Please select" @remove-tag="deleteTag" @change="changeRole" > <el-option v-for="item in roles" :key="item.roleCode" :label="item.roleName" :value="item.id" /> </el-select> </el-form-item> </el-form> <div slot="footer"> <el-button type="text" @click="doCancel">cancel</el-button> <el-button :loading="formLoading" type="primary" @click="doSubmit">confirm</el-button> </div> </el-dialog> <el-tabs v-model="activeName" type="border-card"> <el-tab-pane label="User list" name="userList"> <el-table ref="table" v-loading="loading" :data="users" style="width: 100%; font-size: 12px;" @selection-change="selectionChangeHandler"> <el-table-column type="selection" width="55" /> <el-table-column :show-overflow-tooltip="true" width="150" prop="userName" label="Login account" /> <el-table-column :show-overflow-tooltip="true" width="150" prop="nickName" label="User nickname" /> <el-table-column prop="gender" width="60" label="Gender"> <template slot-scope="scope"> <el-tag v-if="scope.row.gender===1" type="success">male</el-tag> <el-tag v-if="scope.row.gender===2" type="warning">female</el-tag> <el-tag v-if="scope.row.gender===0" type="info">unknown</el-tag> </template> </el-table-column> <el-table-column :show-overflow-tooltip="true" prop="phone" width="150" label="Telephone" /> <el-table-column :show-overflow-tooltip="true" prop="city" label="Location"> <template slot-scope="scope"> <span>{{ scope.row.province }} {{ scope.row.city }} {{ scope.row.country }}</span> </template> </el-table-column> <el-table-column :show-overflow-tooltip="true" prop="avatarUrl" width="80" label="head portrait"> <template slot-scope="scope"> <img :src=" scope.row.avatarUrl ? baseApi + '/file/' + scope.row.avatarUrl : Avatar " > </template> </el-table-column> <el-table-column :show-overflow-tooltip="true" prop="createTime" width="155" label="Date of registration"> <template slot-scope="scope"> <span>{{ parseTime(scope.row.createTime) }}</span> </template> </el-table-column> <el-table-column label="operation" width="160" align="center" fixed="right" > <template slot-scope="scope"> <el-button size="mini" type="text" round @click="doAssignRole(scope.row.id)">Assign roles</el-button> </template> </el-table-column> </el-table> </el-tab-pane> </el-tabs> </el-row> </div> </template> <script> import { mapGetters } from 'vuex' import Avatar from '@/assets/images/avatar.png' import { parseTime } from '@/utils/index' import { getUserList, deleteUser, getInfoById, getUserRoles, saveUserRoles } from '@/api/user' import { getRoleList } from '@/api/role' export default { name: 'User', data() { return { Avatar: Avatar, activeName: 'userList', showDialog: false, loading: false, formLoading: true, form: {}, users: [], selections: [], userName: '', createTime: null, roles: [], userRoles: [] } }, computed: { ...mapGetters([ 'baseApi' ]) }, created() { }, methods: { parseTime, doQuery() { this.users = [] var param = { userName: this.userName } if (this.createTime != null) { param.createTimeStart = Date.parse(this.createTime[0]) param.createTimeEnd = Date.parse(this.createTime[1]) } getUserList(param).then(res => { if (res) { this.users = res } }) }, doDelete() { const ids = [] this.selections.forEach((res) => { ids.push(res.id) }) this.$confirm(`Are you sure to delete these users?`, 'Tips', { confirmButtonText: 'determine', cancelButtonText: 'cancel', type: 'warning' }).then(() => deleteUser(ids).then(res => { if (res) { this.$notify({ title: 'Delete succeeded', type: 'success', duration: 2500 }) this.doQuery() } }) ).catch(() => { }) }, // Select change selectionChangeHandler(val) { this.selections = val }, doAssignRole(id) { this.form = {} this.userRoles = [] this.roles = [] this.showDialog = true this.formLoading = true getInfoById(id).then((res) => { this.form = { id: res.id, userName: res.userName, nickName: res.nickName, gender: res.gender, phone: res.phone } var param = { } getRoleList(param).then(res => { if (res) { this.roles = res } getUserRoles(id).then((res) => { if (res) { res.forEach(role => { this.userRoles.push(role.id) }) } this.formLoading = false }) }) }) }, doCancel() { this.showDialog = false this.form = {} }, doSubmit() { this.formLoading = true saveUserRoles(this.form.id, this.userRoles).then(() => { this.showDialog = false this.$notify({ title: 'Saved successfully', type: 'success', duration: 2500 }) }) }, deleteTag(value) { this.userRoles.forEach(function(data, index) { if (data.id === value) { this.userRoles.splice(index, value) } }) }, changeRole(value) { // console.log(this.userRoles) } } } </script> <style rel="stylesheet/scss" lang="scss"> .avatar { width: 32px; height: 32px; border-radius: 50%; } </style> <style rel="stylesheet/scss" lang="scss" scoped> ::v-deep .el-input-number .el-input__inner { text-align: left; } </style>4, Effect demonstration 5, Source code