权限管理---尚硅谷
1.项目基础
2.定义统一返回结果对象
3.Nodejs
4.前端内容编写
5.菜单详情
6.SpringSecurity权限管理
7.添加登录日志
8.操作日志
9.后端打包
10.前端打包
11.动态sql日期的判断
项目基础
定义统一返回结果对象
- 定义全局统一返回结果类
import lombok.Data;
/**
* 全局统一返回结果类
*
*/
@Data
public class Result<T> {
//返回码
private Integer code;
//返回消息
private String message;
//返回数据
private T data;
public Result(){}
// 返回数据
protected static <T> Result<T> build(T data) {
Result<T> result = new Result<T>();
if (data != null)
result.setData(data);
return result;
}
public static <T> Result<T> build(T body, Integer code, String message) {
Result<T> result = build(body);
result.setCode(code);
result.setMessage(message);
return result;
}
public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
Result<T> result = build(body);
result.setCode(resultCodeEnum.getCode());
result.setMessage(resultCodeEnum.getMessage());
return result;
}
public static<T> Result<T> ok(){
return Result.ok(null);
}
/**
* 操作成功
* @param data baseCategory1List
* @param <T>
* @return
*/
public static<T> Result<T> ok(T data){
Result<T> result = build(data);
return build(data, ResultCodeEnum.SUCCESS);
}
public static<T> Result<T> fail(){
return Result.fail(null);
}
/**
* 操作失败
* @param data
* @param <T>
* @return
*/
public static<T> Result<T> fail(T data){
Result<T> result = build(data);
return build(data, ResultCodeEnum.FAIL);
}
public Result<T> message(String msg){
this.setMessage(msg);
return this;
}
public Result<T> code(Integer code){
this.setCode(code);
return this;
}
}
- 定义统一返回结果状态信息类
import lombok.Getter;
/**
* 统一返回结果状态信息类
*
*/
@Getter
public enum ResultCodeEnum {
SUCCESS(200,"成功"),
FAIL(201, "失败"),
SERVICE_ERROR(2012, "服务异常"),
DATA_ERROR(204, "数据异常"),
ILLEGAL_REQUEST(205, "非法请求"),
REPEAT_SUBMIT(206, "重复提交"),
ARGUMENT_VALID_ERROR(210, "参数校验异常"),
LOGIN_AUTH(208, "未登陆"),
PERMISSION(209, "没有权限"),
ACCOUNT_ERROR(214, "账号不正确"),
PASSWORD_ERROR(215, "密码不正确"),
LOGIN_MOBLE_ERROR( 216, "账号不正确"),
ACCOUNT_STOP( 217, "账号已停用"),
NODE_ERROR( 218, "该节点下有子节点,不可以删除")
;
private Integer code;
private String message;
private ResultCodeEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
- 编写controller,返回对象
Node.js
NPM 构建前端项目-管理依赖
模块化开发
-
设置方法,设置那些方法可以被其他js调用
-
引入js文件,调用其方法
ES6写法
- 定义方法,加上export关键字
- import 的方式 只取需要的方法即可,多个方法用逗号分隔
- 需要进行转码 将自己转为es5 babel 文件 -d 转后的文件名称
es6 的第二种写法
- 定义方法
2. 调用方法
3. 进行转码操作,运行操作
babel es6-2 -d es5-2
babel-node --presets env 2.js
npm run dev
vue修改访问本地端口
角色列表的实现
- 添加角色管理路由
角色开发 创建路由 创建页面
2.Api定义接口
3.调用api方法,获取响应的数据
4.把接口返回的数据进行处理
查询
列表
3. 分页
4. 列表删除
新增方法
修改方法
批量删除
<template>
<div class="app-container">
角色列表
<!--查询表单-->
<div class="search-div">
<el-form label-width="70px" size="small">
<el-row>
<el-col :span="24">
<el-form-item label="角色名称">
<el-input style="width: 100%" v-model="searchObj.roleName" placeholder="角色名称"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row style="display:flex">
<el-button type="primary" icon="el-icon-search" size="mini" @click="fetchData()">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetData">重置</el-button>
</el-row>
</el-form>
</div>
<!-- 工具条 -->
<div class="tools-div">
<el-button type="success" icon="el-icon-plus" size="mini" @click="add">添 加</el-button>
<el-button class="btn-add" size="mini" @click="batchRemove()" >批量删除</el-button>
</div>
<!-- 表格 listLoading 页面加载转圈效果 :data="list" -- list 每页的列表数据 -->
<el-table
v-loading="listLoading"
:data="list"
stripe
border
style="width: 100%;margin-top: 10px;"
@selection-change="handleSelectionChange">// 改变复选框就会触发条件
<el-table-column type="selection"/> // table 组件上添加复选框
<el-table-column
label="序号"
width="70"
align="center">
<template slot-scope="scope">
{{ (page - 1) * limit + scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="roleName" label="角色名称" />
<el-table-column prop="roleCode" label="角色编码" />
<el-table-column prop="createTime" label="创建时间" width="160"/>
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" size="mini" @click="edit(scope.row.id)" title="修改"/>
<el-button type="danger" icon="el-icon-delete" size="mini" @click="removeDataById(scope.row.id)" title="删除"/>
<el-button type="warning" icon="el-icon-baseball" size="mini" @click="showAssignAuth(scope.row)" title="分配权限"/>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<el-pagination
:current-page="page"
:total="total"
:page-size="limit"
style="padding: 30px 0; text-align: center;"
layout="total, prev, pager, next, jumper"
@current-change="fetchData"
/>
<el-dialog title="添加/修改" :visible.sync="dialogVisible" width="40%" >
<el-form ref="dataForm" :model="sysRole" label-width="150px" size="small" style="padding-right: 40px;">
<el-form-item label="角色名称">
<el-input v-model="sysRole.roleName"/>
</el-form-item>
<el-form-item label="角色编码">
<el-input v-model="sysRole.roleCode"/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small" icon="el-icon-refresh-right">取 消</el-button>
<el-button type="primary" icon="el-icon-check" @click="saveOrUpdate()" size="small">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
//引入定义接口的js文件
import api from '@/api/system/role'
export default {
//定义初始值/初始变量
data() {
return {
listLoading:false,//是否显示加载
list:[],//角色列表
total:0,//总记录数
page:1,//当前页
limit:3,//每页显示记录数
searchObj:{},//条件查询封装对象
dialogVisible: false,//弹出框
sysRole:{}, //封装添加表单数据
selectValue:[] //复选框选择内容封装数组
}
},
//页面渲染之前执行
created() {
//调用列表方法
this.fetchData()
},
methods:{//具体方法
//跳转分配菜单权限
showAssignAuth(row) {
this.$router.push('/system/assignAuth?id='+row.id+'&roleName='+row.roleName);
},
//复选框发生变化执行方法
handleSelectionChange(selection) {
this.selectValue = selection
//console.log(this.selectValue)
},
//批量删除
batchRemove() {
//判断
if(this.selectValue.length==0) {
this.$message.warning('请选择要删除的记录!')
return
}
this.$confirm('此操作将永久删除该角色, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
//数组
var idList = []
//获取多个复选框对应id,封装到数组里面
// [1,2,3]
for(var i=0;i<this.selectValue.length;i++) {
var obj = this.selectValue[i]
//id值
var id = obj.id
//放到数组里面
idList.push(id)
}
api.batchRemove(idList).then(response => {
//提示
this.$message({
type: 'success',
message: '删除成功!'
});
//刷新
this.fetchData()
})
})
},
//修改-数据回显
edit(id) {
//弹出框
this.dialogVisible = true
api.getRoleId(id).then(response => {
this.sysRole = response.data
})
},
//点击确定
saveOrUpdate() {
//判断添加还是修改
if(!this.sysRole.id) {
//添加
this.saveRole()
} else {
this.updateRole()
}
},
//修改的方法
updateRole() {
api.update(this.sysRole)
.then(response => {
//提示
this.$message({
type: 'success',
message: '修改成功!'
});
//关闭弹框
this.dialogVisible = false
//刷新页面
this.fetchData()
})
},
//添加的方法
saveRole() {
api.saveRole(this.sysRole)
.then(response => {
//提示
this.$message({
type: 'success',
message: '添加成功!'
});
//关闭弹框
this.dialogVisible = false
//刷新页面
this.fetchData()
})
},
//点击添加,弹出框
add() {
this.dialogVisible = true
this.sysRole = {}
},
//删除
removeDataById(id) {
this.$confirm('此操作将永久删除该角色, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
//调用方法删除
api.removeId(id)
.then(response => {
//提示
this.$message({
type: 'success',
message: '删除成功!'
});
//刷新
this.fetchData()
})
})
},
//重置
resetData() {
//清空表单
this.searchObj = {}
//查询所有数据
this.fetchData()
},
//条件分页查询列表
//pageNum 查询页数
fetchData(pageNum=1) {
//页数赋值
this.page = pageNum
//ajax
api.getPageList(this.page,this.limit,this.searchObj)
.then(response => { // 调用接口成功执行then方法
//this.listLoading = false
//console.log(response)
//每页数据列表
this.list = response.data.records
//总记录数
this.total = response.data.total
})
}
}
}
</script>
api
import request from '@/utils/request'
//常量
const api_name = '/admin/system/sysUser'
export default {
//列表
getPageList(page,limit,searchObj) {
return request({
//接口路径
url: `${api_name}/${page}/${limit}`,
method: 'get', //提交方式
//参数
params: searchObj
})
},
//添加
save(user) {
return request({
//接口路径
url: `${api_name}/save`,
method: 'post', //提交方式
//参数
data: user
})
},
//根据id查询
getUserId(id) {
return request({
//接口路径
url: `${api_name}/getUser/${id}`,
method: 'get' //提交方式
})
},
//修改
update(user) {
return request({
//接口路径
url: `${api_name}/update`,
method: 'post', //提交方式
//参数
data: user // 将结果转为list进行传递
})
},
//删除
removeById(id) {
return request({
//接口路径
url: `${api_name}/remove/${id}`,
method: 'delete' //提交方式
})
},
//更改用户状态
updateStatus(id,status) {
return request({
//接口路径
url: `${api_name}/updateStatus/${id}/${status}`,
method: 'get' //提交方式
})
},
}
禁用启用
//更改用户状态
switchStatus(row) {
//判断,如果当前用户可用,修改禁用
row.status = row.status === 1 ? 0 : 1
api.updateStatus(row.id,row.status)
.then(response => {
this.$message.success(response.message || '操作成功')
this.fetchData()
})
},
<el-table-column prop="username" label="用户名" width="180"/>
<el-table-column prop="name" label="姓名" width="110"/>
<el-table-column prop="phone" label="手机" />
<el-table-column label="状态" width="80">
<template slot-scope="scope">
<el-switch
v-model="scope.row.status===1"
@change="switchStatus(scope.row)">
</el-switch>
</template>
</el-table-column>
//更改用户状态
updateStatus(id,status) {
return request({
//接口路径
url: `${api_name}/updateStatus/${id}/${status}`,
method: 'get' //提交方式
})
},
菜单管理
代码实现
当前节点下存在子节点使用递归的方式
import com.qwq.model.system.SysMenu;
import java.util.ArrayList;
import java.util.List;
public class MenuHelper {
// 构建树形结构
public static List<SysMenu> bulidTree(List<SysMenu> list) {
List<SysMenu> trees = new ArrayList<>();
for (SysMenu sysMenu : list) {
//先得到根节点进行遍历
if (sysMenu.getParentId().longValue() == 0) {
trees.add(findChildren(sysMenu,list));
}
}
return trees;
}
/**
* 递归查找子节点
* @param treeNodes
* @return
*/
public static SysMenu findChildren(SysMenu sysMenu, List<SysMenu> treeNodes) {
sysMenu.setChildren(new ArrayList<SysMenu>());
// sysMenu = 根节点 treeNodes = 全部数据集合
// 遍历全部数据
for (SysMenu it : treeNodes) {
String id = sysMenu.getId();
long ids = Long.parseLong(id);
// 全部数据的ParentId
Long parentId = it.getParentId();
// 根节点 id == 遍历数据的parentId
// 2 == 3 3 == 6
if (ids == parentId) {
System.out.println(ids +"============"+ it.getId());
sysMenu.getChildren().add(findChildren(it , treeNodes));
}
}
return sysMenu;
}
}
权限管理
关于安全方面的两个核心功能是“认证”和“授权”,一般来说,Web 应用的安全性包括**用户认证(Authentication)和用户授权(Authorization)**两个部分,这两点也是 SpringSecurity 重要核心功能。
(1)用户认证指的是:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码,系统通过校验用户名和密码来完成认证过程。
通俗点说就是系统认为用户是否能登录
(2)用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。
通俗点讲就是系统判断用户是否有权限去做某些事情。
用户认证流程
添加登录日志
验证登录的拦截器登录成功后,调用service , request转化为ip , 进行新增处理
public class IpUtil {
public static String getIpAddress(HttpServletRequest request) {
String ipAddress = null;
try {
ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1")) {
// 根据网卡取本机配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress = inet.getHostAddress();
}
}
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
// = 15
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
} catch (Exception e) {
ipAddress="";
}
// ipAddress = this.getRequest().getRemoteAddr();
return ipAddress;
}
public static String getGatwayIpAddress(ServerHttpRequest request) {
HttpHeaders headers = request.getHeaders();
String ip = headers.getFirst("x-forwarded-for");
if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
// 多次反向代理后会有多个ip值,第一个ip才是真实ip
if (ip.indexOf(",") != -1) {
ip = ip.split(",")[0];
}
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = headers.getFirst("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = headers.getFirst("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = headers.getFirst("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = headers.getFirst("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = headers.getFirst("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddress().getAddress().getHostAddress();
}
return ip;
}
}
操作日志
后端打包
pom文件新增build标签
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<div id = ‘10’ / > 前端打包
输入命令进行打包
日期
格式:
<!--mapper的全限定类名-->
<mapper namespace="com.qwq.system.mapper.SysMenuMapper">
<resultMap id="sysMenuMap" type="com.qwq.model.system.SysMenu" autoMapping="true">
</resultMap>
<!-- 用于select查询公用抽取的列 -->
<sql id="columns">
m.id,m.parent_id,m.name,m.type,m.path,m.component,m.perms,m.icon,m.sort_value,m.status,m.create_time,m.update_time,m.is_deleted
</sql>
<select id="findListByUserId" resultMap="sysMenuMap">
select
distinct <include refid="columns" />
from sys_menu m
inner join sys_role_menu rm on rm.menu_id = m.id
inner join sys_user_role ur on ur.role_id = rm.role_id
where
ur.user_id = #{userId}
and m.status = 1
and rm.is_deleted = 0
and ur.is_deleted = 0
and m.is_deleted = 0
</select>
</mapper>