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


本文目录

  • 一、前言
  • 二、后端实现
    • 2.1 创建用户实体类
    • 2.2 创建用户角色实体类
    • 2.3 添加用户与用户角色Mapper接口
    • 2.4 实现用户的增删改查服务
    • 2.5 编写Controller层
  • 三、前端实现
    • 3.1 添加用户api访问接口
    • 3.2 编写前端页面
  • 四、效果演示
  • 五、源码

一、前言

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

  • 本文将实现用户的管理功能,重点实现用户的角色分配。
    – 用户的新增(自助注册)可参考以下文章:《手把手教你通过SpringBoot实现邮箱注册码验证》

  • 实现效果如下:

二、后端实现

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;@JsonIgnoreprivate 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;@Emailprivate String email;private String phone;private String remarks;@TableLogicprivate Boolean enabled;private Timestamp lastPasswordResetTime;private Timestamp createTime;@Builder.Defaultprivate 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("更新用户信息失败");}@Overridepublic SysUser findById(Long id) {return sysUserMapper.selectById(id);}@Overridepublic SysUser findByUserName(String userName) {return sysUserMapper.selectOne(new QueryWrapper<SysUser>().lambda().eq(SysUser::getUserName, userName));}@Overridepublic boolean registerEmailExist(String email) {QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();queryWrapper.lambda().eq(SysUser::getEmail, email);return sysUserMapper.selectOne(queryWrapper) != null;}@Overridepublic 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());}};}@Overridepublic List<PermissionDto> getUserPermission(Long userId) {return sysUserMapper.selectUserPermission(userId);}@Overridepublic 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("找不到当前登录的信息");}@Overridepublic 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("批量删除用户")@DeleteMappingpublic 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-inputv-model="userName"size="small"clearableplaceholder="输入账号或用户名称搜索"style="width: 200px"class="filter-item"@keyup.enter.native="doQuery"/><el-date-pickerv-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-buttonclass="filter-item"size="mini"type="success"icon="el-icon-search"@click="doQuery">搜索</el-button><el-buttonclass="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-selectv-model="userRoles"style="width: 455px"multiplefilterableplaceholder="请选择"@remove-tag="deleteTag"@change="changeRole"><el-optionv-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-columnlabel="操作"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 = truethis.formLoading = truegetInfoById(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 = falsethis.form = {}},doSubmit() {this.formLoading = truesaveUserRoles(this.form.id, this.userRoles).then(() => {this.showDialog = falsethis.$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>

四、效果演示

五、源码

  • 前端
    https://gitee.com/zhuhuix/startup-frontend
    https://github.com/zhuhuix/startup-frontend
  • 后端
    https://gitee.com/zhuhuix/startup-backend
    https://github.com/zhuhuix/startup-backend

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

  1. SpringBoot整合SpringSecurity+Redis权限控制

    SpringBoot整合SpringSecurity+Redis权限控制 1.认识SpringSecurity 2.效果截图 2.1.登录接口 2.2.注册接口 2.3.管理员权限接口 2.4.普通用 ...

  2. SpringBoot整合Shiro实现权限控制,验证码

    本文介绍 SpringBoot 整合 shiro,相对于 Spring Security 而言,shiro 更加简单,没有那么复杂. 目前我的需求是一个博客系统,有用户和管理员两种角色.一个用户可能有 ...

  3. springboot整合security实现权限控制

    1.建表,五张表,如下: 1.1.用户表 CREATE TABLE `t_sys_user` (`user_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT ...

  4. springboot整合shiro实现权限控制

    博主简介:原互联网大厂tencent员工,网安巨头Venustech员工,阿里云开发社区专家博主,微信公众号java基础笔记优质创作者,csdn优质创作博主,创业者,知识共享者,欢迎关注,点赞,收藏. ...

  5. linux 权限控制(用户管理、用户组管理、文件权限)

    文章目录 1. 用户管理 1.1 useradd 添加新用户并在home目录下创建对应用户的目录 1.2 passwd 设置用户密码 1.3 id 查看用户是否存在 1.4 cat /etc/pass ...

  6. vue-element-admin整合spring-boot实现权限控制之用户管理篇

    vue-element-admin整合spring-boot实现权限控制之用户管理篇 0 引言 距离笔者上次利用vue-element-admin项目整合后台spring-boot项目打通前后端权限控 ...

  7. springboot整合springsecurity安全框架(后端spring_security模块代码可直接使用,根据需求自定义修改)

    SpringSecurity简介 最下面有与springboot整合的模块代码 用户认证和用户授权 主要包含两部分:用户认证和用户授权 用户认证:进入用户登录时候,输入用户名密码,查询数据库查看是否正 ...

  8. Springboot整合SpringSecurity 04-启用登出logout功能

    Springboot整合SpringSecurity 04-启用登出logout功能 前面Springboot整合SpringSecurity 02-使用自定义登陆页面我们讲过了SpringSecur ...

  9. Springboot 整合SpringSecurity实现账号密码+手机验证码登陆

    Springboot 整合SpringSecurity实现账号密码+手机验证码登陆 示例说明 版本 示例安装 Spring-security 介绍 为什么不用 shiro Spring-Securit ...

最新文章

  1. 面试官问我:什么是高并发下的请求合并?
  2. led动态显示 c语言,单片机LED点阵的纵向移动(动态显示)
  3. Drupal 通过API动态的添加样式文件
  4. Java基础入门笔记-整数+小数+字符串+打印
  5. 老手萌新学习composer的使用
  6. python 一组数据 正态分布散点图_python高维数据型图表矩阵散点图
  7. duilib菜单动态添加
  8. 3D建模和渲染的硬件配置怎么选?这里有答案
  9. MATLAB机器人可视化运动仿真
  10. ERP系统BOM详细解析
  11. python 学习(八—1) 项目:生成随机的测试试卷文件
  12. c语言四象限线性差值算法
  13. 如何做好一名合格的项目组长
  14. sqlDBX 链接 mysql 提示ODBC驱动不正确
  15. 二叉树最强总结(python实现)
  16. 《单片机原理及应用(魏洪磊)》第六章第12题
  17. js 实现一个简单的存钱/取钱/查询/退出等操作的ATM功能.
  18. Matlab矩阵的定义与构建
  19. Windows 10系统下安装Go语言环境
  20. Git 里面的 origin 到底代表什么意思?【转载】

热门文章

  1. Sort Colors - LeetCode
  2. 牛客每日训练----加边的无向图,美丽的项链,勇敢的妞妞
  3. 2016,不忘初心;2017,方得始终!
  4. 03-树2 List Leaves
  5. matlab 绘制椭圆锥波束指向示意图
  6. VS2015调试的自动窗口在哪里
  7. response对象
  8. JSP数据交互:request、response对象
  9. A+CLUB 2022第十期 | 量化专题金秋会
  10. PBAC基于策略的权限控制