SpringBoot整合SpringSecurity实现权限控制(五):用户管理

系列文章目录
《SpringBoot整合SpringSecurity实现权限控制(一):实现原理》
《SpringBoot整合SpringSecurity实现权限控制(二):权限数据基本模型设计》
《SpringBoot整合SpringSecurity实现权限控制(三):前端动态装载路由与菜单》
《SpringBoot整合SpringSecurity实现权限控制(四):角色管理》



一、前言

系统的使用者称为用户,仅可在被赋予的权限范围之内操作系统。
管理员是一种特殊的用户,拥有系统操作的最高权限。

在这里插入图片描述

  • 实现效果如下:
    在这里插入图片描述

二、后端实现

2.1 创建用户实体类

  • 该实体类对应用户表,记录用户的基础信息。
/**
 * 用户表
 *
 * @author zhuhuix
 * @date 2020-04-03
 */
@ApiModel(value = "用户信息")
@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;

    /**
     * 性别 0-未知 1-male,2-female
     */
    private Integer gender;

    /**
     * 头像地址
     */
    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 创建用户角色实体类

  • 该实体类对应用户角色表,记录用户对应的角色信息(一个角户可对应多个角色)。
    在这里插入图片描述
/**
 * 用户角色表
 *
 * @author zhuhuix
 * @date 2021-09-29
 */
@ApiModel(value = "用户角色信息")
@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 添加用户与用户角色Mapper接口

  • 通过该Mapper接口可操作用户实体与用户角色实体
/**
 * 用户DAO接口
 *
 * @author zhuhuix
 * @date 2021-07-19
 */
@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
    
    

    /**
     * 查询用户角色
     * @param userId 用户id
     * @return 角色信息
     */
    @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=#{userId} ")
    List<SysRole> selectUserRoles(Long userId);

    /**
     * 查询用户权限
     * @param userId 用户id
     * @return 权限信息
     */
    @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=#{userId}"+
            " and m.enabled=1 " +
            " order by m.sort "
    )
    List<PermissionDto> selectUserPermission(Long userId);
}

/**
 * 用户角色DAO接口
 *
 * @author zhuhuix
 * @date 2021-09-29
 */
@Mapper
public interface SysUserRoleMapper extends BaseMapper<SysUserRole> {
    
    
}

2.4 实现用户的增删改查服务

/**
 * 用户管理服务接口
 *
 * @author zhuhuix
 * @date 2020-04-03
 */
public interface SysUserService {
    
    

    /**
     * 增加用户
     *
     * @param user 待新增的用户
     * @return 增加成功的用户
     */
    SysUser create(SysUser user);

    /**
     * 删除用户
     *
     * @param user 待删除的用户
     * @return 删除成功的用户
     */
    SysUser delete(SysUser user);

    /**
     * 修改用户
     *
     * @param user 待修改的用户
     * @return 修改成功的用户
     */
    SysUser update(SysUser user);

    /**
     * 根据id查找用户
     *
     * @param id 用户id
     * @return 用户信息
     */
    SysUser findById(Long id);

    /**
     * 根据userName查找用户
     *
     * @param userName 用户帐号
     * @return 用户帐号对应的用户
     */
    SysUser findByUserName(String userName);

    /**
     * 判断注册使用的邮箱是否存在
     *
     * @param email 邮箱号
     * @return 是否找到
     */
    boolean registerEmailExist(String email);

    /**
     * 获取用户信息
     *
     * @return 用户信息
     */
    UserDetails getUserInfo();

    /**
     * 修改用户头像
     *
     * @param file 文件
     * @return json
     */
    Map<String, String> updateAvatar(MultipartFile file);

    /**
     * 获取用户角色信息
     *
     * @param userId 用户id
     * @return 角色信息
     */
    List<SysRole> getUserRoles(Long userId);

    /**
     * 保存用户角色
     *
     * @param userId 用户id
     * @param roleIds 角色id列表
     * @return 是否成功
     */
    Boolean saveUserRoles(Long userId,Set<Long> roleIds);

    /**
     * 获取用户权限信息
     *
     * @param userId 用户id
     * @return 权限信息
     */
    List<PermissionDto> getUserPermission(Long userId);

    /**
     * 根据条件查询用户信息
     *
     * @param sysUserQueryDto 查询条件
     * @return 用户列表
     */
    List<SysUser> list(SysUserQueryDto sysUserQueryDto);

    /**
     * 批量删除用户
     *
     * @param ids 待删除的用户id列表
     * @return 是否成功
     */
    Boolean delete(Set<Long> ids);

}
/**
 * 用户管理服务实现类
 *
 * @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("增加用户信息失败");
    }

    @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("删除用户信息失败");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysUser update(SysUser user) {
    
    
        if (sysUserMapper.updateById(user) > 0) {
    
    
            return user;
        }
        throw new RuntimeException("更新用户信息失败");
    }

    @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) {
    
    
        // 首先清除该用户原有的角色信息
        QueryWrapper<SysUserRole> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(SysUserRole::getUserId, userId);
        sysUserRoleMapper.delete(queryWrapper);
        // 再进行添加
        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("登录状态已过期");
        }
        if (authentication.getPrincipal() instanceof UserDetails) {
    
    
            UserDetails userDetails = (UserDetails) authentication.getPrincipal();
            return (userDetails.getUsername());
        }
        throw new RuntimeException("找不到当前登录的信息");
    }

    @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("删除用户信息失败");
    }
}

2.5 编写Controller层

  • 共实现以下9个后台接口
    在这里插入图片描述
/**
 * api用户信息
 *
 * @author zhuhuix
 * @date 2021-08-16
 */
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/api/user")
@Api(tags = "用户信息接口")
public class SysUserController {
    
    

    private final SysUserService sysUserService;

    @ApiOperation("获取当前登录用户信息")
    @GetMapping()
    public ResponseEntity<Object> getUserInfo() {
    
    
        return ResponseEntity.ok(sysUserService.getUserInfo());
    }

    @ApiOperation("根据id获取用户信息")
    @GetMapping("/{id}")
    public ResponseEntity<Object> getUserInfo(@PathVariable Long id) {
    
    
        return ResponseEntity.ok(sysUserService.findById(id));
    }

    @ApiOperation("更新用户信息")
    @PostMapping()
    public ResponseEntity<Object> saveUser(@RequestBody SysUser user) {
    
    
        return ResponseEntity.ok(sysUserService.update(user));
    }

    @PreAuthorize("hasAuthority('user:updateAvatar')")
    @ApiOperation("修改用户头像")
    @PostMapping(value = "/updateAvatar")
    public ResponseEntity<Object> updateAvatar(@RequestParam MultipartFile avatar) {
    
    
        return ResponseEntity.ok(sysUserService.updateAvatar(avatar));
    }

    @ApiOperation("根据条件查询用户列表")
    @PostMapping("/list")
    public ResponseEntity<Object> getSysUserList(@RequestBody SysUserQueryDto sysUserQueryDto) {
    
    
        return ResponseEntity.ok(sysUserService.list(sysUserQueryDto));
    }

    @ApiOperation("批量删除用户")
    @DeleteMapping
    public ResponseEntity<Object> deleteUsers(@RequestBody Set<Long> ids) {
    
    
        return ResponseEntity.ok(sysUserService.delete(ids));
    }

    @ApiOperation("获取用户角色")
    @GetMapping("/roles/{userId}")
    public ResponseEntity<Object> getUserRoles(@PathVariable Long userId) {
    
    
        return ResponseEntity.ok(sysUserService.getUserRoles(userId));
    }

    @ApiOperation("保存用户角色")
    @PostMapping("/roles/{userId}")
    public ResponseEntity<Object> saveUserRoles(@PathVariable Long userId, @RequestBody Set<Long> ids) {
    
    
        return ResponseEntity.ok(sysUserService.saveUserRoles(userId, ids));
    }

    @ApiOperation("获取用户权限")
    @GetMapping("/permission/{userId}")
    public ResponseEntity<Object> getUserPermission(@PathVariable Long userId) {
    
    
        return ResponseEntity.ok(sysUserService.getUserPermission(userId));
    }

}

三、前端实现

3.1 添加用户api访问接口

import request from '@/utils/request'

// 登录
export function login(data) {
    
    
  return request({
    
    
    url: '/api/auth/login',
    method: 'post',
    data
  })
}

// 注销
export function logout() {
    
    
  return request({
    
    
    url: '/api/auth/logout',
    method: 'delete'
  })
}

// 获取当前登录用户信息
export function getInfo() {
    
    
  return request({
    
    
    url: '/api/user',
    method: 'get'
  })
}

// 根据用户id获取用户信息
export function getInfoById(id) {
    
    
  return request({
    
    
    url: '/api/user/' + id,
    method: 'get'
  })
}

// 保存并更新用户
export function saveUser(data) {
    
    
  return request({
    
    
    url: '/api/user',
    method: 'post',
    data
  })
}

// 根据用户id列表批量删除用户
export function deleteUser(ids) {
    
    
  return request({
    
    
    url: '/api/user',
    method: 'delete',
    data: ids
  })
}

// 根据条件查询获取用户列表
export function getUserList(params) {
    
    
  return request({
    
    
    url: '/api/user/list',
    method: 'post',
    data: JSON.stringify(params)
  })
}

// 根据用户id获取用户权限
export function getUserPermission(userId) {
    
    
  return request({
    
    
    url: '/api/user/permission/' + userId,
    method: 'get'
  })
}

// 获取用户角色
export function getUserRoles(userId) {
    
    
  return request({
    
    
    url: '/api/user/roles/' + userId,
    method: 'get'
  })
}

// 分配用户角色
export function saveUserRoles(userId, roleIds) {
    
    
  return request({
    
    
    url: '/api/user/roles/' + userId,
    method: 'post',
    data: roleIds
  })
}

3.2 编写前端页面

  • 我们需要编写一个完整的页面:
  1. 通过登录账号或用户名,注册起始与结束时间查询用户,并以列表的形式进行显示。
    在这里插入图片描述

  2. 通过点击“分配角色”按钮,跳出表单,选取角色信息给用户进行角色分配。
    在这里插入图片描述

  3. 在列表上选取用户(可多选),点击“删除”按钮,删除选取的用户,删除前要有提示。
    在这里插入图片描述

  • 前端关键源码如下:
    – src/user/index.vue
<template>
  <div class="app-container">
    <!--工具栏-->
    <div class="head-container">
      <!-- 搜索 -->
      <el-input
        v-model="userName"
        size="small"
        clearable
        placeholder="输入账号或用户名称搜索"
        style="width: 200px"
        class="filter-item"
        @keyup.enter.native="doQuery"
      />
      <el-date-picker
        v-model="createTime"
        :default-time="['00:00:00', '23:59:59']"
        type="daterange"
        range-separator=":"
        size="small"
        class="date-item"
        value-format="yyyy-MM-dd HH:mm:ss"
        start-placeholder="开始日期"
        end-placeholder="结束日期"
      />
      <el-button
        class="filter-item"
        size="mini"
        type="success"
        icon="el-icon-search"
        @click="doQuery"
      >搜索</el-button>
      <el-button
        class="filter-item"
        size="mini"
        type="danger"
        icon="el-icon-circle-plus-outline"
        :disabled="selections.length===0"
        @click="doDelete"
      >删除</el-button>
    </div>

    <el-row>
      <!--角色分配表单-->
      <el-dialog append-to-body :close-on-click-modal="false" :visible.sync="showDialog" title="角色分配" width="600px">
        <el-form ref="form" :inline="true" :model="form" size="small" label-width="76px">
          <el-form-item label="登录账号" prop="userName">
            <el-input v-model="form.userName" :disabled="true" />
          </el-form-item>
          <el-form-item label="昵称" prop="nickName">
            <el-input v-model="form.nickName" :disabled="true" />
          </el-form-item>
          <el-form-item style="margin-bottom: 0;" label="角色" prop="userRoles">
            <el-select
              v-model="userRoles"
              style="width: 455px"
              multiple
              filterable
              placeholder="请选择"
              @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" class="dialog-footer">
          <el-button type="text" @click="doCancel">取消</el-button>
          <el-button :loading="formLoading" type="primary" @click="doSubmit">确认</el-button>
        </div>
      </el-dialog>
      <el-tabs v-model="activeName" type="border-card">
        <el-tab-pane label="用户列表" 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="登录账号" />
            <el-table-column :show-overflow-tooltip="true" width="150" prop="nickName" label="用户昵称" />
            <el-table-column prop="gender" width="60" label="性别">
              <template slot-scope="scope">
                <el-tag v-if="scope.row.gender===1" type="success"></el-tag>
                <el-tag v-if="scope.row.gender===2" type="warning"></el-tag>
                <el-tag v-if="scope.row.gender===0" type="info">未知</el-tag>
              </template>
            </el-table-column>
            <el-table-column :show-overflow-tooltip="true" prop="phone" width="150" label="电话" />
            <el-table-column :show-overflow-tooltip="true" prop="city" label="所在地区">
              <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="头像">
              <template slot-scope="scope">
                <img
                  :src="
                    scope.row.avatarUrl
                      ? baseApi + '/file/' + scope.row.avatarUrl
                      : Avatar
                  "
                  class="avatar"
                >
              </template>
            </el-table-column>
            <el-table-column :show-overflow-tooltip="true" prop="createTime" width="155" label="注册日期">
              <template slot-scope="scope">
                <span>{
    
    {
    
     parseTime(scope.row.createTime) }}</span>
              </template>
            </el-table-column>
            <el-table-column
              label="操作"
              width="160"
              align="center"
              fixed="right"
            >
              <template slot-scope="scope">
                <el-button size="mini" type="text" round @click="doAssignRole(scope.row.id)">分配角色</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(`确认删除这些用户吗?`, '提示', {
    
    
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() =>
        deleteUser(ids).then(res => {
    
    
          if (res) {
    
    
            this.$notify({
    
    
              title: '删除成功',
              type: 'success',
              duration: 2500
            })
            this.doQuery()
          }
        })
      ).catch(() => {
    
    
      })
    },
    // 选择改变
    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: '保存成功',
          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>

四、效果演示

在这里插入图片描述

五、源码

猜你喜欢

转载自blog.csdn.net/jpgzhu/article/details/120450964