系列目录

SpringSecurity权限管理系统实战—一、项目简介和开发环境准备

SpringSecurity权限管理系统实战—二、日志、接口文档等实现

SpringSecurity权限管理系统实战—三、主要页面及接口实现

SpringSecurity权限管理系统实战—四、整合SpringSecurity(上)

SpringSecurity权限管理系统实战—五、整合SpringSecurity(下)

SpringSecurity权限管理系统实战—六、SpringSecurity整合jwt

SpringSecurity权限管理系统实战—七、处理一些问题

前言

​本篇文章的内容有点杂,搞得我都不知道怎么取标题了。

​上次我们已经搭建好了my-springsecurity-plus的基本环境,本次我们我们要实现功能有系统日志配置、配置swagger接口文档、配置druid连接池等

一、Banner替换

​可以有些第一次接触到这个名词的小伙伴不清楚banner是什么,其实就是在运行springboot项目时控制台打印出的图案,就是下面这个东西。

  .   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.1.RELEASE)

​这下是不是就熟悉了,其实SpringBoot支持自定义banner图案。只需要放在指定位置,SpringBoot会帮我们自动替换。Spring Boot 默认寻找 Banner 的顺序是:

  1. 依次在 Classpath 下找 文件 banner.gif , banner.jpg , 和 banner.png , 先找到谁就用谁。
  2. 继续 Classpath 下找 banner.txt
  3. 上面都没有找到的话, 用默认的 SpringBootBanner

我们只需要在 src/main/resources 下新建一个 banner.txt,然后找一个在线生成banner的网站,例如patorjk,然后将生成的文本复制到banner.txt文件中。启动项目,查看控制台

是不是很炫酷,一个知名项目的banner是这样的

////////////////////////////////////////////////////////////////////
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //
////////////////////////////////////////////////////////////////////

二、日志

​在项目的开发中,日志是必不可少的一个记录事件的组件。应该很多不少刚入门的小伙伴对日志都是不怎么重视,对于我来说也是这样,即使现在我对日志也不是很重视,也没有养成记录日志的习惯。但其实日志在一个系统中尤为的重要,可以帮助快速定位bug,来保证服务的高可用。

​Spring Boot默认使用LogBack日志系统,如果不需要更改为其他日志系统如Log4j2等,则无需多余的配置,LogBack默认将日志打印到控制台上。

​而Spring Boot项目一般都会引用spring-boot-starter或者spring-boot-starter-web的依赖,这两个依赖中包含了spring-boot-starter-logging的依赖,所以我们如果不使用别的日志框架,无需修改依赖。

​如果我们要使用日志功能,只需要在相应类上加上@Slf4j(需要lambok插件)注解,在对应方法中log.indf(),log.error()等就可以输出日志。我们把HelloController改造成如下这样

@Controller
@Slf4j
public class HelloController { @GetMapping(value = "/index")
public String index(){
log.info("测试");
log.error("测试");
return "index";
}
@GetMapping(value = "/login")
public String login(){
return "login";
}
@GetMapping(value = "/console/console1")
public String console1(){
return "console/console1";
}
}

​重启项目,访问http://localhost:8080/index控制台会打印如下信息

​那么如何把日志存贮到文件里呢?我们只要在application.yml中简单定义一下

logging:
file:
path: src\main\resources\logger\ # logger文件夹需要提前生成

​启动项目,会在logger目录下生成一个spring.log文件,内容和控制台输出的一致。

​日志的输出格式支持自定义,但是自定义后在控制台输出的内容就不是彩色的了,当然也能定义成彩色的,还有日志文件生成的大小(总不能一直存在一个文件里吧,那不就无限大了)和存储时间等等,都可以自定义。我这里不详细介绍了,有兴趣的小伙伴可以自己了解。

三、Swagger接口文档

​Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。Swagger让部署管理和使用功能强大的API变得非常简单。官方网站:http://swagger.io/。

​Swagger也可以用来测试接口(很多人会用postman,但是swagger可能用起来更简单一点)

​那么我们首先要在maven添加相关依赖

		<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!--swagger ui-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>

​ 这个我上一章给的依赖中有了,不要重复添加,这里只是为了说明。

​ 在启动类的那一层级中新建config包,在其中新建SwaggerConfig类

@Configuration//表明这是一个配置类
@EnableSwagger2//开启Swagger
public class SwaggerConfig {
@Bean
public Docket webApiConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")//组名称
.apiInfo(webApiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.codermy.myspringsecurityplus.controller"))//扫描的包
.paths(PathSelectors.any())
.build(); }
/**
* 该套 API 说明,包含作者、简介、版本、等信息
* @return
*/
private ApiInfo webApiInfo(){
return new ApiInfoBuilder()
.title("my-springsecurity-plus-API文档")
.description("本文档描述了my-springsecurity-plus接口定义")
.version("1.0")
.build();
} }

​ 然后我们访问http://localhost:8080/swagger-ui.html

​ 接口的名字也可以自定义,详细见Swagger 常用注解使用详解

​ 我们再改造一下HelloController

@Controller
@Slf4j
@Api(tags = "前期测试后面会删")
public class HelloController { @GetMapping(value = "/index")
public String index(){
return "index";
} @GetMapping(value = "/login")
public String login(){
return "login";
} @GetMapping(value = "/console/console1")
@ApiOperation(value = "转发console1请求")
public String console1(){
return "console/console1";
}
}

重启访问

四、主要界面接口

接下来我们把用户管理,角色管理,和权限管理三个界面的的接口换成我们自己的。

首先我们新建一个类来统一返回数据格式,新建utiils包,在其中新建Result类

//统一返回结果的类
@Data
public class Result<T> implements Serializable { @ApiModelProperty(value = "是否成功")
private Boolean success; @ApiModelProperty(value = "返回码")
private Integer code; @ApiModelProperty(value = "返回消息")
private String msg; @ApiModelProperty(value = "总数")
private Integer count; @ApiModelProperty(value = "返回数据")
private List<T> data = new ArrayList<T>(); //把构造方法私有
private Result() {} public static Result table_sucess() {
Result r = new Result();
r.setSuccess(true);
r.setCode(ResultCode.TABLE_SUCCESS);
r.setMsg("成功");
return r;
} //成功静态方法
public static Result ok() {
Result r = new Result();
r.setSuccess(true);
r.setCode(ResultCode.SUCCESS);
r.setMsg("成功");
return r;
} //失败静态方法
public static Result error() {
Result r = new Result();
r.setSuccess(false);
r.setCode(ResultCode.ERROR);
r.setMsg("失败");
return r;
}
public Result success(Boolean success){
this.setSuccess(success);
return this;
}
public Result message(String message){
this.setMsg(message);
return this;
}
public Result code(Integer code){
this.setCode(code);
return this;
} public Result data(List<T> list){
this.data.addAll(list);
return this;
}
public Result count(Integer count){
this.count = count;
return this;
}
}

在新建一个ReslutCode接口来定义常用的状态码

public interface ResultCode {
/**
* 请求t成功
*/
public static Integer SUCCESS = 200;
/**
* 请求table成功
*/
public static Integer TABLE_SUCCESS = 0;
/**
* 请求失败
*/
public static Integer ERROR = 201; /**
* 请求已经被接受
*/
public static final Integer ACCEPTED = 202; /**
* 操作已经执行成功,但是没有返回数据
*/
public static final Integer NO_CONTENT = 204; /**
* 资源已被移除
*/
public static final Integer MOVED_PERM = 301; /**
* 重定向
*/
public static final Integer SEE_OTHER = 303; /**
* 资源没有被修改
*/
public static final Integer NOT_MODIFIED = 304; /**
* 参数列表错误(缺少,格式不匹配)
*/
public static final Integer BAD_REQUEST = 400; /**
* 未授权
*/
public static final Integer UNAUTHORIZED = 401; /**
* 访问受限,授权过期
*/
public static final Integer FORBIDDEN = 403; /**
* 资源,服务未找到
*/
public static final Integer NOT_FOUND = 404; /**
* 不允许的http方法
*/
public static final Integer BAD_METHOD = 405; /**
* 资源冲突,或者资源被锁
*/
public static final Integer CONFLICT = 409; /**
* 不支持的数据,媒体类型
*/
public static final Integer UNSUPPORTED_TYPE = 415; /**
* 接口未实现
*/
public static final Integer NOT_IMPLEMENTED = 501;
}

自定义异常处理(这里不过多解释,只简单实现直接贴代码

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
//指定处理什么异常
@ExceptionHandler(Exception.class)
@ResponseBody
public Result error(Exception e){
e.printStackTrace();
return Result.error().message("执行了全局异常");
}
//自定义异常
@ExceptionHandler(MyException.class)
@ResponseBody
public Result error(MyException e){
log.error(e.getMessage());
e.printStackTrace();
return Result.error().code(e.getCode()).message(e.getMsg());
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyException extends RuntimeException {
private Integer code;//状态码
private String msg;//异常信息
}

新建PageTableRequest 分页工具类

@Data
public class PageTableRequest implements Serializable { private Integer page;//初始页
private Integer limit;//一页几条数据
private Integer offset;//页码 public void countOffset(){
if(null == this.page || null == this.limit){
this.offset = 0;
return;
}
this.offset = (this.page - 1) * limit;
} }

下面进入正题

因为我这里用的是druid的连接池(之后介绍),我直接把application.yml贴出来

server:
port: 8080
spring:
profiles:
active: dev
application:
name: my-springsecurity-plus
datasource:
driver:
driver-class-name: com.mysql.cj.jdbc.Driver
# 后面时区不要忘了如果你是mysql8.0以上的版本
url: jdbc:mysql://localhost:3306/my-springsecurity-plus?serverTimezone=Asia/Shanghai
username: root
password: 180430121
type: com.alibaba.druid.pool.DruidDataSource #druid连接池之后会解释这里先复制
druid:
# 初始化配置
initial-size: 3
# 最小连接数
min-idle: 3
# 最大连接数
max-active: 15
# 获取连接超时时间
max-wait: 5000
# 连接有效性检测时间
time-between-eviction-runs-millis: 90000
# 最大空闲时间
min-evictable-idle-time-millis: 1800000
test-while-idle: true
test-on-borrow: false
test-on-return: false
validation-query: select 1
# 配置监控统计拦截的filters
filters: stat
web-stat-filter:
url-pattern: /*
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
#StatViewServlet配置,说明请参考Druid Wiki,配置_StatViewServlet配置
stat-view-servlet:
enabled: true #是否启用StatViewServlet默认值true
url-pattern: /druid/*
reset-enable: true
login-username: admin
login-password: admin
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8 # mybatis配置
mybatis:
type-aliases-package: com.codermy.myspringsecurityplus.entity
mapper-locations: classpath:/mybatis-mappers/*
configuration:
map-underscore-to-camel-case: true logging:
file:
path: src\main\resources\logger\ # logger文件夹需要提前生成

用户管理菜单接口,之前应该都创建好了相应的类,只拿这一个接口做例子,另外两个都一样

MyUser实体类

@Data
@EqualsAndHashCode(callSuper = true)
public class MyUser extends BaseEntity<Integer>{
private static final long serialVersionUID = -6525908145032868837L;
private String userName;
private String password;
private String nickName;
private String phone;
private String email;
private Integer status;
public interface Status {
int LOCKED = 0;
int VALID = 1;
}
}

UserDao中新建两个方法,分页会用到

@Mapper
public interface UserDao {
//分页返回所有用户
@Select("SELECT * FROM my_user t ORDER BY t.id LIMIT #{startPosition}, #{limit}")
List<MyUser> getAllUserByPage(@Param("startPosition")Integer startPosition,@Param("limit")Integer limit); //计算所有用户数量
@Select("select count(*) from My_user")
Long countAllUser();
}

UserService和UserServiceImlpl

public interface UserService {
Result<MyUser> getAllUsersByPage(Integer startPosition, Integer limit);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao; @Override
public Result<MyUser> getAllUsersByPage(Integer startPosition, Integer limit) { return Result.ok().count(userDao.countAllUser().intValue()).data(userDao.getAllUserByPage(startPosition,limit)).code(ResultCode.TABLE_SUCCESS);
}
}

UserController

@Controller
@RequestMapping("/api/user")
@Api(tags = "用户相关接口")
public class UserController {
@Autowired
private UserService userService; @GetMapping
@ResponseBody
@ApiOperation(value = "用户列表")
public Result<MyUser> index(PageTableRequest pageTableRequest){
pageTableRequest.countOffset();
return userService.getAllUsersByPage(pageTableRequest.getOffset(),pageTableRequest.getLimit());
}
}

我们可以比较一下他需要的json(user.json,在admin/data/user.json)和我们返回的json格式

他原先设置空值的可以不看,说明也用不着,然后在usr.html中把对应相同的数据,但是命名不一样的地方修改一下即可。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" th:href="@{/PearAdmin/component/layui/css/layui.css}" />
<link rel="stylesheet" th:href="@{/PearAdmin/admin/css/pearCommon.css}"/>
</head>
<body class="pear-container">
<div class="layui-card">
<div class="layui-card-body">
<form class="layui-form" action="">
<div class="layui-form-item">
<label class="layui-form-label">用户名</label>
<div class="layui-input-inline">
<input type="text" name="nickName" placeholder="" class="layui-input">
</div>
<label class="layui-form-label">账号</label>
<div class="layui-input-inline">
<input type="text" name="userName" placeholder="" class="layui-input">
</div>
<label class="layui-form-label">地点</label>
<div class="layui-input-inline">
<select name="city" lay-verify="required">
<option value=""></option>
<option value="0">北京</option>
<option value="1">上海</option>
<option value="2">广州</option>
<option value="3">深圳</option>
<option value="4">杭州</option>
</select>
</div>
<button class="pear-btn pear-btn-md pear-btn-primary" lay-submit lay-filter="user-query">
<i class="layui-icon layui-icon-search"></i>
查询
</button>
<button type="reset" class="pear-btn pear-btn-md">
<i class="layui-icon layui-icon-refresh"></i>
重置
</button>
</div>
</form>
</div>
</div>
<div class="layui-card">
<div class="layui-card-body">
<table id="user-table" lay-filter="user-table"></table>
</div>
</div> <script type="text/html" id="user-toolbar">
<button class="pear-btn pear-btn-primary pear-btn-md" lay-event="add">
<i class="layui-icon layui-icon-add-1"></i>
新增
</button>
<button class="pear-btn pear-btn-danger pear-btn-md" lay-event="batchRemove">
<i class="layui-icon layui-icon-delete"></i>
删除
</button>
</script> <script type="text/html" id="user-bar">
<button class="pear-btn pear-btn-primary pear-btn-sm" lay-event="edit"><i class="layui-icon layui-icon-edit"></i></button>
<button class="pear-btn pear-btn-danger pear-btn-sm" lay-event="remove"><i class="layui-icon layui-icon-delete"></i></button>
</script> <script type="text/html" id="user-status">
<input type="checkbox" name="status" value="{{d.id}}" lay-skin="switch" lay-text="启用|禁用" lay-filter="user-status" checked = "{{ d.status == 0 ? 'true' : 'false' }}">
</script> <script type="text/html" id="user-createTime">
{{layui.util.toDateString(d.createTime, 'yyyy-MM-dd HH:mm:ss')}}
</script> <script th:src="@{/PearAdmin/component/layui/layui.js}" charset="utf-8"></script>
<script>
layui.use(['table','form','jquery'],function () {
let table = layui.table;
let form = layui.form;
let $ = layui.jquery;
let MODULE_PATH = "operate/";
//这里对应的field要和自己返回的json名称一致
let cols = [
[
{type:'checkbox'},
{title: '账号', field: 'userName', align:'center', width:100},
{title: '姓名', field: 'nickName', align:'center'},
{title: '电话', field: 'phone', align:'center'},
{title: '邮箱', field: 'email', align:'center'},
{title: '启用', field: 'status', align:'center', templet:'#user-status'},
{title: '创建时间', field: 'createTime', align:'center',templet:'#user-createTime'},
{title: '操作', toolbar: '#user-bar', align:'center', width:130}
]
] table.render({
elem: '#user-table',
url: '/api/user',//+++++++++++看这里 这里的url换成自己接口的url++++++++++++++
page: true ,
cols: cols ,
skin: 'line',
toolbar: '#user-toolbar',
defaultToolbar: [{
layEvent: 'refresh',
icon: 'layui-icon-refresh',
}, 'filter', 'print', 'exports']
}); table.on('tool(user-table)', function(obj){
if(obj.event === 'remove'){
window.remove(obj);
} else if(obj.event === 'edit'){
window .edit(obj);
}
}); table.on('toolbar(user-table)', function(obj){
if(obj.event === 'add'){
window.add();
} else if(obj.event === 'refresh'){
window.refresh();
} else if(obj.event === 'batchRemove'){
window.batchRemove(obj);
}
}); form.on('submit(user-query)', function(data){
table.reload('user-table',{where:data.field})
return false;
}); form.on('switch(user-status)', function(obj){
layer.tips(this.value + ' ' + this.name + ':'+ obj.elem.checked, obj.othis);
}); window.add = function(){
layer.open({
type: 2,
title: '新增',
shade: 0.1,
area: ['500px', '400px'],
content: MODULE_PATH + 'add.html'
});
}
window.edit = function(obj){
layer.open({
type: 2,
title: '修改',
shade: 0.1,
area: ['500px', '400px'],
content: MODULE_PATH + 'edit.html'
});
} window.remove = function(obj){
layer.confirm('确定要删除该用户', {icon: 3, title:'提示'}, function(index){
layer.close(index);
let loading = layer.load();
$.ajax({
url: MODULE_PATH+"remove/"+obj.data['id'],
dataType:'json',
type:'delete',
success:function(result){
layer.close(loading);
if(result.success){
layer.msg(result.msg,{icon:1,time:1000},function(){
obj.del();
});
}else{
layer.msg(result.msg,{icon:2,time:1000});
}
}
})
});
} window.batchRemove = function(obj){
let data = table.checkStatus(obj.config.id).data;
if(data.length === 0){
layer.msg("未选中数据",{icon:3,time:1000});
return false;
}
let ids = "";
for(let i = 0;i<data.length;i++){
ids += data[i].id+",";
}
ids = ids.substr(0,ids.length-1);
layer.confirm('确定要删除这些用户', {icon: 3, title:'提示'}, function(index){
layer.close(index);
let loading = layer.load();
$.ajax({
url: MODULE_PATH+"batchRemove/"+ids,
dataType:'json',
type:'delete',
success:function(result){
layer.close(loading);
if(result.success){
layer.msg(result.msg,{icon:1,time:1000},function(){
table.reload('user-table');
});
}else{
layer.msg(result.msg,{icon:2,time:1000});
}
}
})
});
} window.refresh = function(param){
table.reload('user-table');
}
})
</script>
</body>
</html>

这样当我们再次点击用户管理时,访问的就是自己的接口了

原本自己看别人的教学博客时,是真的希望人家把所有的代码一字不差的贴上来。等到自己写的时候就觉得还是有道理的,代码太占篇幅了,还影响博客的观感。所以另外两个界面我就补贴代码了,大家仿照这个来就行。



放两张图片,让大家看一下改完的效果。



数据库文件和同步代码在giteegithub中可以获取

五、Druid连接池

Druid是阿里开源的数据库连接池,作为后起之秀,性能比dbcp、c3p0更高,使用也越来越广泛。

当然Druid不仅仅是一个连接池,还有很多其他的功能。

druid的优点

  • 高性能。性能比dbcp、c3p0高很多。
  • 只要是jdbc支持的数据库,druid都支持,对数据库的支持性好。并且Druid针对oracle、mysql做了特别优化。
  • 提供监控功能。可以监控sql语句的执行时间、ResultSet持有时间、返回行数、更新行数、错误次数、错误堆栈等信息,来了解连接池、sql语句的工作情况,方便统计、分析SQL的执行性能

如何使用??

导入依赖,之前给的依赖中就有不用重复导入

		<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

application.yml中配置

spring:
profiles:
active: dev
application:
name: my-springsecurity-plus
datasource:
driver:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/my-springsecurity-plus?serverTimezone=Asia/Shanghai
username: root
password: 180430121
type: com.alibaba.druid.pool.DruidDataSource
druid:
# 初始化配置
initial-size: 3
# 最小连接数
min-idle: 3
# 最大连接数
max-active: 15
# 获取连接超时时间
max-wait: 5000
# 连接有效性检测时间
time-between-eviction-runs-millis: 90000
# 最大空闲时间
min-evictable-idle-time-millis: 1800000
test-while-idle: true
test-on-borrow: false
test-on-return: false validation-query: select 1
# 配置监控统计拦截的filters
filters: stat
web-stat-filter:
url-pattern: /*
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
#StatViewServlet配置,说明请参考Druid Wiki,配置_StatViewServlet配置
stat-view-servlet:
enabled: true #是否启用StatViewServlet默认值true
url-pattern: /druid/*
reset-enable: true
login-username: admin #用户名
login-password: admin #密码

更详细的配置这里就不介绍了。

然后重启项目访问http://localhost:8080/druid/login.html输入用户名密码就可以看到界面了。



呼,终于又写完一篇,写代码的时候真没感觉这么累,像我这种文笔差的经常写着写着就把自己写乱了。。。。。

SpringSecurity权限管理系统实战—二、日志、接口文档等实现的更多相关文章

  1. SpringSecurity权限管理系统实战—八、AOP 记录用户、异常日志

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  2. SpringSecurity权限管理系统实战—一、项目简介和开发环境准备

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  3. SpringSecurity权限管理系统实战—四、整合SpringSecurity(上)

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  4. SpringSecurity权限管理系统实战—六、SpringSecurity整合jwt

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  5. SpringSecurity权限管理系统实战—七、处理一些问题

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  6. SpringSecurity权限管理系统实战—九、数据权限的配置

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  7. SpringSecurity权限管理系统实战—三、主要页面及接口实现

    系列目录 前言 后端五分钟,前端半小时.. 每次写js都头疼. 自己写前端是不可能的,这辈子不可能自己写前端的,只能找找别人的模板才能维持的了生存这样子.github,gitee上的模板又多,帮助文档 ...

  8. SpringSecurity权限管理系统实战—五、整合SpringSecurity(下)

    系列目录 前言 上篇文章SpringSecurity整合了一半,这次把另一半整完,所以本篇的序号接着上一篇. 七.自定义用户信息 前面我们登录都是用的指定的用户名和密码或者是springsecurit ...

  9. 我的第一个python web开发框架(23)——代码版本控制管理与接口文档

    书接上一回,小白和老菜聊到代码的版本控制和接口文档 小白:为什么要做版本控制,我不弄版本控制不也完成了项目了吗?要做版本控制不是很麻烦,又要安装服务又要提交代码,代码又不是多人用开发,还要写文档... ...

随机推荐

  1. Python 简明教程 --- 26,Python 多进程编程

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 学编程最有效的方法是动手敲代码. 目录 1,什么是多进程 我们所写的Python 代码就是一个程序, ...

  2. NFS /etc/exports参数解释

    nfs 安装 执行以下命令安装 nfs 服务器所需的软件包 yum install -y nfs-utils 执行命令 vim /etc/exports,创建 exports 文件,文件内容如下: / ...

  3. laravel5.5 安装

    环境要求 PHP >= 7.0.0 PHP OpenSSL 扩展 PHP PDO 扩展 PHP Mbstring 扩展 PHP Tokenizer 扩展 PHP XML 扩展 通过 Larave ...

  4. variable ans might not have been initialized 报错,以及初始化注意点

    他是说你没有初始化而已,一般只是个warning,如果是在不能跑,那就给他初始化一下. 注意,初始化可不是任意值哈! 就比如如果要算阶乘,你初始化就不能为0. 还有如果是比较大小这类,就不要把初始化统 ...

  5. 微服务迁移记(五):WEB层搭建(1)

    WEB层是最终表现层,注册至注册中心,引用接口层(不需要引用实现层).公共服务层.用户登录使用SpringSecurity,Session保存在redis中,权限管理没有用SpringSecurity ...

  6. MacOS 键盘符号和修饰键说明

    原文链接:https://www.cnblogs.com/exmyth/p/5949192.html   Mac键盘符号和修饰键说明 ⌘ Command ⇧ Shift ⌥ Option ⌃ Cont ...

  7. Vue中数组元素被替换,页面没有动态展示

    原始代码 页面没有相应goodsList替换,打印goodsList数据已经被替换: (借用https://www.cnblogs.com/belongs-to-qinghua/p/11112613. ...

  8. PHP jdtogregorian() 函数

    ------------恢复内容开始------------ 实例 把格利高里历法的日期转换为儒略日计数,然后再转换回格利高里历法的日期: <?php$jd=gregoriantojd(6,20 ...

  9. JDK8的Optional用法

    参考资料:https://www.baeldung.com/java-optional https://mp.weixin.qq.com/s/P2kb4fswb4MHfb0Vut_kZg 1. 描述 ...

  10. MySQL中EXPLAIN命令详细解析

    很多情况下我们需要知道某条SQL语句的性能,都会通过EXPLAIN命令来查看查询优化器是如何执行的. 如何使用 使用EXPLAIN很简单,只需要在执行的SQL前面加上EXPLAIN即可 explain ...