Spring Security rights management system combat - 2. Implementation of logs, interface documents, etc.

Series catalog

Spring Security rights management system combat - 1. Project introduction and development environment preparation
Spring Security permission management system combat - 2. Implementation of logs, interface documents, etc.
SpringSecurity rights management system combat - three, the main page and interface implementation
SpringSecurity rights management system combat - four, integration of SpringSecurity (on)
SpringSecurity rights management system combat - 5. Integrate SpringSecurity (below)
SpringSecurity rights management system combat - six, SpringSecurity integration jwt
Spring Security rights management system combat - seven, deal with some problems

foreword

The content of this article is a bit complicated, so I don't know how to choose the title.

​Last time we have built the basic environment of my-springsecurity-plus. This time we want to implement functions such as system log configuration, configuration of swagger interface documents, configuration of druid connection pool, etc.

1. Banner replacement

​Maybe some friends who come into contact with this term for the first time do not know what banner is. In fact, it is the pattern printed by the console when running the springboot project, which is the following thing.

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

Are you familiar with it now? In fact, SpringBoot supports custom banner patterns. Just put it in the specified location, and SpringBoot will automatically replace it for us. The default order in which Spring Boot looks for Banner is:

  1. Find the files banner.gif , banner.jpg , and banner.png in the Classpath in turn, and use whoever finds it first.
  2. Continue to find banner.txt under Classpath
  3. If none of the above are found, use the default SpringBootBanner

We just need to create a new banner.txt under src/main/resources, and then find a website that generates banners online, for example patorjk , then copy the resulting text into the banner.txt file. Start the project and view the console

Isn't it cool, the banner of a well-known project is like this

////////////////////////////////////////////////////////////////////
//                          _ooOoo_                               //
//                         o8888888o                              //
//                         88" . "88                              //
//                         (| ^_^ |)                              //
//                         O\  =  /O                              //
//                      ____/`---'\____                           //
//                    .'  \\|     |//  `.                         //
//                   /  \\|||  :  |||//  \                        //
//                  /  _||||| -:- |||||-  \                       //
//                  |   | \\\  -  /// |   |                       //
//                  | \_|  ''\---/''  |   |                       //
//                  \  .-\__  `-`  ___/-. /                       //
//                ___`. .'  /--.--\  `. . ___                     //
//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
//      ========`-.____`-.___\_____/___.-`____.-'========         //
//                           `=---='                              //
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
//            Buddha bless, never downtime, never BUG //
////////////////////////////////////////////////////////////////////

2. Log

​In the development of the project, the log is an indispensable component for recording events. There should be many friends who are just getting started who don't pay much attention to logs, and the same is true for me. Even now I don't pay much attention to logs, and I haven't developed the habit of recording logs. But in fact, logs are particularly important in a system, which can help quickly locate bug s and ensure high availability of services.

​Spring Boot uses the LogBack logging system by default. If you do not need to change to other logging systems such as Log4j2, no extra configuration is required. LogBack prints logs to the console by default.

​The Spring Boot project generally refers to the dependencies of spring-boot-starter or spring-boot-starter-web. These two dependencies include the dependencies of spring-boot-starter-logging, so if we do not use other logging frameworks , no need to modify dependencies.

​If we want to use the log function, we only need to add the @Slf4j (lambok plugin) annotation to the corresponding class, and log.indf(),log.error(), etc. can be output in the corresponding method. We transform HelloController into the following

@Controller
@Slf4j
public class HelloController {

    @GetMapping(value = "/index")
    public String index(){
        log.info("test");
        log.error("test");
        return "index";
    }   
    @GetMapping(value = "/login")
    public String login(){
        return "login";
    }
    @GetMapping(value = "/console/console1")
    public String console1(){
        return "console/console1";
    }
}

​Restart the project, visit http://localhost:8080/index The console will print the following information

​So how do you store logs in a file? We just need to simply define it in application.yml

logging:
  file:
    path: src\main\resources\logger\ # The logger folder needs to be generated in advance

​Starting the project will generate a spring.log file in the logger directory with the same content as the console output.

​The output format of the log supports customization, but the content output in the console will not be colored after customization. Of course, it can also be defined as colored, and the size of the log file generated (it can’t always exist in a file, right? That is not infinite) and storage time, etc., can be customized. I won't go into details here, and interested friends can find out for themselves.

3. Swagger interface documentation

​Swagger is a canonical and complete framework for generating, describing, invoking and visualizing RESTful Web services. The overall goal is to have the client and filesystem update at the same rate as the server. File methods, parameters and models are tightly integrated into the server-side code, allowing the API to be always in sync. Swagger makes it easy to deploy, manage and consume powerful API s. Official website: http://swagger.io/.
​Swagger can also be used to test interfaces (many people use postman, but swagger may be easier to use)

​Then we first need to add related dependencies in 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>

​ This is in the dependency I gave in the previous chapter, don't add it repeatedly, it's just for illustration.

​ Create a new config package at the level of the startup class, and create a new SwaggerConfig class in it

@Configuration//Indicates that this is a configuration class
@EnableSwagger2//Open Swagger
public class SwaggerConfig {
    @Bean
    public Docket webApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")//group name
                .apiInfo(webApiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.codermy.myspringsecurityplus.controller"))//scanned package
                .paths(PathSelectors.any())
                .build();

    }
    /**
     * This set of API descriptions, including author, introduction, version, and other information
     * @return
     */
    private ApiInfo webApiInfo(){
        return new ApiInfoBuilder()
                .title("my-springsecurity-plus-API Documentation")
                .description("This document describes my-springsecurity-plus Interface definition")
                .version("1.0")
                .build();
    }

}

​ Then we visit http://localhost:8080/swagger-ui.html

​ The name of the interface can also be customized. For details, see Swagger's common annotations are explained in detail

​ Let’s transform HelloController again

@Controller
@Slf4j
@Api(tags = "It will be deleted after the preliminary test")
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 = "Forward console1 ask")
    public String console1(){
        return "console/console1";
    }
}

restart access

Fourth, the main interface interface

Next, we replace the interfaces of the three interfaces of user management, role management, and permission management with our own.

First, we create a new class to uniformly return the data format, create a new utiils package, and create a new Result class in it

//The class that returns the result uniformly
@Data
public class Result<T> implements Serializable {

    @ApiModelProperty(value = "whether succeed")
    private Boolean success;

    @ApiModelProperty(value = "return code")
    private Integer code;

    @ApiModelProperty(value = "return message")
    private String msg;

    @ApiModelProperty(value = "total")
    private Integer count;

    @ApiModelProperty(value = "return data")
    private List<T> data = new ArrayList<T>();

    //make the constructor private
    private Result() {}

    public static Result table_sucess() {
        Result r = new Result();
        r.setSuccess(true);
        r.setCode(ResultCode.TABLE_SUCCESS);
        r.setMsg("success");
        return r;
    }

    //success static method
    public static Result ok() {
        Result r = new Result();
        r.setSuccess(true);
        r.setCode(ResultCode.SUCCESS);
        r.setMsg("success");
        return r;
    }

    //fail static method
    public static Result error() {
        Result r = new Result();
        r.setSuccess(false);
        r.setCode(ResultCode.ERROR);
        r.setMsg("fail");
        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;
    }
}

Create a new ReslutCode interface to define common status codes

public interface ResultCode {
    /**
     * request t succeeded
     */
    public static Integer SUCCESS = 200;
    /**
     * Request table success
     */
    public static Integer TABLE_SUCCESS = 0;
    /**
     * Request failed
     */
    public static Integer ERROR = 201;

    /**
     * request has been accepted
     */
    public static final Integer ACCEPTED = 202;

    /**
     * The operation was performed successfully, but no data was returned
     */
    public static final Integer NO_CONTENT = 204;

    /**
     * resource has been removed
     */
    public static final Integer MOVED_PERM = 301;

    /**
     * redirect
     */
    public static final Integer SEE_OTHER = 303;

    /**
     * The resource has not been modified
     */
    public static final Integer NOT_MODIFIED = 304;

    /**
     * parameter list error (missing, format mismatch)
     */
    public static final Integer BAD_REQUEST = 400;

    /**
     * unauthorized
     */
    public static final Integer UNAUTHORIZED = 401;

    /**
     * Access restricted, authorization expired
     */
    public static final Integer FORBIDDEN = 403;

    /**
     * resource, service not found
     */
    public static final Integer NOT_FOUND = 404;

    /**
     * http method not allowed
     */
    public static final Integer BAD_METHOD = 405;

    /**
     * Resource conflict, or the resource is locked
     */
    public static final Integer CONFLICT = 409;

    /**
     * Unsupported data, media type
     */
    public static final Integer UNSUPPORTED_TYPE = 415;

    /**
     * interface not implemented
     */
    public static final Integer NOT_IMPLEMENTED = 501;
}

Custom exception handling (I won't explain too much here, just simply paste the code directly

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    //Specifies what exception to handle
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result error(Exception e){
        e.printStackTrace();
        return Result.error().message("global exception executed");
    }
    //custom exception
    @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;//status code
    private String  msg;//exception information
}

Create a new PageTableRequest paging tool class

@Data
public class PageTableRequest implements Serializable {

    private Integer page;//start page
    private Integer limit;//Several pieces of data per page
    private Integer offset;//page number

    public void countOffset(){
        if(null == this.page || null == this.limit){
            this.offset = 0;
            return;
        }
        this.offset = (this.page - 1) * limit;
    }

}

Enter the topic below

Because I am using druid's connection pool (introduced later), I will paste application.yml directly

server:
  port: 8080
spring:
  profiles:
    active: dev
  application:
    name: my-springsecurity-plus
  datasource:
    driver:
    driver-class-name: com.mysql.cj.jdbc.Driver
    # Don't forget the back time zone if you are a version above mysql8.0
    url: jdbc:mysql://localhost:3306/my-springsecurity-plus?serverTimezone=Asia/Shanghai
    username: root
    password: 180430121
    type: com.alibaba.druid.pool.DruidDataSource #After the druid connection pool, it will be explained here to copy first
    druid:
      # Initial configuration
      initial-size: 3
      # Minimum number of connections
      min-idle: 3
      # Maximum number of connections
      max-active: 15
      # Get connection timeout
      max-wait: 5000
      # Connection validity detection time
      time-between-eviction-runs-millis: 90000
      # maximum idle time
      min-evictable-idle-time-millis: 1800000
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      validation-query: select 1
      # Configure filters for monitoring statistics interception
      filters: stat
      web-stat-filter:
        url-pattern: /*
        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
      #StatViewServlet configuration, please refer to Druid Wiki, configuration_StatViewServlet configuration
      stat-view-servlet:
        enabled: true #Whether to enable StatViewServlet default value 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 configuration
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\ # The logger folder needs to be generated in advance


User management menu interface, the corresponding classes should have been created before, just take this interface as an example, the other two are the same

MyUser entity class

@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;
    }
}

There are two new methods in UserDao, which will be used for paging

@Mapper
public interface UserDao {
    //Paginate to return all users
    @Select("SELECT * FROM my_user t ORDER BY t.id LIMIT #{startPosition}, #{limit}")
    List<MyUser> getAllUserByPage(@Param("startPosition")Integer startPosition,@Param("limit")Integer limit);

    //count all users
    @Select("select count(*) from My_user")
    Long countAllUser();
}

UserService and 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 = "User related interface")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping
    @ResponseBody
    @ApiOperation(value = "user list")
    public Result<MyUser> index(PageTableRequest pageTableRequest){
        pageTableRequest.countOffset();
        return userService.getAllUsersByPage(pageTableRequest.getOffset(),pageTableRequest.getLimit());
    }
}

We can compare the json he needs (user.json, in admin/data/user.json) with the json format we return

If he originally set a null value, he can ignore it, and the description is not needed. Then, he can modify the place corresponding to the same data but with a different name in 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">username</label>
						<div class="layui-input-inline">
							<input type="text" name="nickName" placeholder="" class="layui-input">
						</div>
						<label class="layui-form-label">account</label>
						<div class="layui-input-inline">
							<input type="text" name="userName" placeholder="" class="layui-input">
						</div>
						<label class="layui-form-label">Place</label>
						<div class="layui-input-inline">
							<select name="city" lay-verify="required">
							        <option value=""></option>
							        <option value="0">Beijing</option>
							        <option value="1">Shanghai</option>
							        <option value="2">Guangzhou</option>
							        <option value="3">Shenzhen</option>
							        <option value="4">Hangzhou</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>
							Inquire
						</button>
						<button type="reset" class="pear-btn pear-btn-md">
							<i class="layui-icon layui-icon-refresh"></i>
							reset
						</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>
		        new
		    </button>
		    <button class="pear-btn pear-btn-danger pear-btn-md" lay-event="batchRemove">
		        <i class="layui-icon layui-icon-delete"></i>
		        delete
		    </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="enable|disabled" 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/";
                //The corresponding field here should be the same as the json name returned by yourself
		        let cols = [
		            [
		                {type:'checkbox'},
		                {title: 'account', field: 'userName', align:'center', width:100},
		                {title: 'Name', field: 'nickName', align:'center'},
		                {title: 'Telephone', field: 'phone', align:'center'},
						{title: 'Mail', field: 'email', align:'center'},
		                {title: 'enable', field: 'status', align:'center', templet:'#user-status'},
		                {title: 'creation time', field: 'createTime', align:'center',templet:'#user-createTime'},
		                {title: 'operate', toolbar: '#user-bar', align:'center', width:130}
		            ]
		        ]
		
		        table.render({
		            elem: '#user-table',
		            url: '/api/user',//+++++++++++Look here Replace the url here with the url of your own interface++++++++++++++
		            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: 'new',
		                shade: 0.1,
		                area: ['500px', '400px'],
		                content: MODULE_PATH + 'add.html'
		            });
		        }
		        window.edit = function(obj){
		            layer.open({
		                type: 2,
		                title: 'Revise',
		                shade: 0.1,
		                area: ['500px', '400px'],
		                content: MODULE_PATH + 'edit.html'
		            });
		        }
		
		        window.remove = function(obj){
		            layer.confirm('Are you sure you want to delete this user', {icon: 3, title:'hint'}, 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("data not selected",{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('Are you sure you want to delete these users', {icon: 3, title:'hint'}, 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>

In this way, when we click on user management again, we will access our own interface.

Originally, when I read other people's teaching blogs, I really hoped that they would paste all the codes verbatim. When I write it myself, I think it makes sense. The code takes up too much space and affects the look and feel of the blog. So I will subsidize the code for the other two interfaces, and everyone can follow this.


Put two pictures, let everyone see the effect of the modification.


The database file and synchronization code are in gitee and github available in

Five, Druid connection pool

Druid is Alibaba's open source database connection pool. As a rising star, its performance is higher than that of dbcp and c3p0, and it is more and more widely used.

Of course Druid is not just a connection pool, there are many other functions.

Advantages of druid

  • high performance. The performance is much higher than dbcp and c3p0.
  • As long as it is a database supported by jdbc, druid supports it, and the support for the database is good. And Druid has been specially optimized for oracle and mysql.
  • Provides monitoring functions. You can monitor the execution time of the sql statement, the holding time of the ResultSet, the number of returned rows, the number of updated rows, the number of errors, the error stack and other information, to understand the connection pool, the working status of the sql statement, and to facilitate statistics and analysis of the execution performance of SQL

how to use??

Import dependencies, there is no need to import repeatedly in the dependencies given before

		<!--druid connection pool-->
        <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>

Configuration in 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 configuration
      initial-size: 3
      # Minimum number of connections
      min-idle: 3
      # Maximum number of connections
      max-active: 15
      # Get connection timeout
      max-wait: 5000
      # Connection validity detection time
      time-between-eviction-runs-millis: 90000
      # maximum idle time
      min-evictable-idle-time-millis: 1800000
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false

      validation-query: select 1
      # Configure filters for monitoring statistics interception
      filters: stat
      web-stat-filter:
        url-pattern: /*
        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
      #StatViewServlet configuration, please refer to Druid Wiki, configuration_StatViewServlet configuration
      stat-view-servlet:
        enabled: true #Whether to enable StatViewServlet default value true
        url-pattern: /druid/*
        reset-enable: true
        login-username: admin #username
        login-password: admin #password

More detailed configuration is not introduced here.
Then restart project access http://localhost:8080/druid/login.html Enter the user name and password to see the interface.

Huh, I finally finished writing another one. I really didn't feel so tired when writing code. Poor writing like me often messes up when I write. . . ..

Tags: Back-end Spring Security

Posted by MattDunbar on Sun, 22 May 2022 07:57:28 +0300