Series directory

SpringSecurity rights management system actual combat – one, project introduction and development environment preparation

SpringSecurity rights management system practice – two, log, interface documents and other implementation

SpringSecurity rights management system practice – three, the main page and interface implementation

SpringSecurity rights management system actual combat – four, integration of SpringSecurity (part 1)

SpringSecurity rights management system practice – five, integration of SpringSecurity (under)

SpringSecurity authority management system combat – six, SpringSecurity integration JWT

SpringSecurity rights management system practice – seven, deal with some problems

SpringSecurity rights management system practice – eight, AOP record user logs, abnormal logs

SpringSecurity rights management system actual combat – nine, data rights configuration

preface

The content of this article is so mixed that I don’t know how to choose the title.

Last time we set up the basic environment of my-SpringSecurity-Plus, this time we will implement the system log configuration, configure swagger interface documents, configure druid connection pool, etc

First, Banner replacement

For those of you who are new to this term and don’t know what a banner is, it’s actually the pattern that the console prints when you run the SpringBoot project, which is this thing down here.

____ ____ _ /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \ / _ ` | \ \ \ \ \ \ / ___) | | _) | | | | | | | (_ | |))))There comes' | |. __ _ - | | | _ _ - | | | _ \ __, | / / / / = = = = = = = = = | _ | = = = = = = = = = = = = = = = | ___ / / _ / _ / _ / : : Spring the Boot: : (v2.3.1. RELEASE)Copy the code

SpringBoot supports custom banner patterns. Just put it in the right place and SpringBoot will automatically replace it for us. Spring Boot defaults to finding banners in the following order:

  1. Find the files banner.gif, banner.jpg, and banner.png in Classpath, whichever one you find first.
  2. Go to the Classpath to find banner.txt
  3. If none of the above is found, use the default SpringBootBanner

All we need to do is create a new banner. TXT file under SRC /main/resources, find a website that generates banners online, such as patorjk, and copy the generated text to our banner. TXT file. Start the project and view the console

Isn’t it cool that the banner of a well-known project looks like this

////////////////////////////////////////////////////////////////////
//                          _ooOoo_                               //
//                         o8888888o                              //
//                         88"."88                              //
//                         (| ^_^ |)                              //
//                         O\  =  /O                              //
//                      ____/`---'there comes \ / / / /.'\ \ | | / / `. / / / / / \ \ | | | "| | | / / / / / / / / _ | | | | | - : - | | | | | - / / / / / | | \ \ \ / / / | | / / / / | \ _ |' '\---/' '| | / / / / \. - \ __ ` ` ___ - / - / / / / / ___ `.'/-- --\'.. ___ // //.""< `. ___ \ _ < | > _ / ___.'>'"". / / / / | | : ` - \ `; ` \ _ / `; . ` / - ` : | | / / / / \ \ ` - \ _ __ \ / __ _ /. - ` / / / / / / = = = = = = = = ` - ____ ` - ___ \ _____ / ___ - ` _____....'= = = = = = = = / / / / ` = - ='/ / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / the Buddha bless Never goes down Never BUG / / ////////////////////////////////////////////////////////////////////Copy the code

Second, the log

In the development of a project, the log is an essential component to record events. Should be a lot of entry-level partners are not how much attention to the log, for me, even now I do not attach great importance to the log, also did not develop the habit of logging. However, logs are especially important in a system to help locate bugs quickly and ensure high availability of services.

The Spring Boot uses the LogBack log system by default. If you do not need to change it to another log system, such as Log4j2, you do not need to configure the LogBack log system. The LogBack logs are printed to the console by default.

Spring Boot projects usually refer to spring-boot-starter or spring-boot-starter-web dependencies, which include the spring-boot-starter-logging dependency. So we don’t need to change dependencies if we don’t use another logging framework.

If we want to use logging, we just need to annotate @slf4j (lambok plug-in required) on the corresponding class, and log. Indf (),log.error(), etc. Let’s make the HelloController look like this

@Controller
@Slf4j
public class HelloController {

    @GetMapping(value = "/index")
    public String index(a){
        log.info("Test");
        log.error("Test");
        return "index";
    }   
    @GetMapping(value = "/login")
    public String login(a){
        return "login";
    }
    @GetMapping(value = "/console/console1")
    public String console1(a){
        return "console/console1"; }}Copy the code

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

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

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

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 can be customized, but the content of the console output after customization is not in color, of course, can be defined in color, as well as the size of the log file generation (not always stored in a file, that is, not forever) and the storage time, etc., can be customized. Here I do not introduce in detail, interested partners can understand their own.

Swagger interface document

Swagger is a canonical and complete framework for generating, describing, invoking, and visualizing RESTful Web services. The overall goal is to have clients and file systems update at the same rate as servers. File methods, parameters, and models are tightly integrated into server-side code, allowing the API to always be in sync. Swagger makes it easy to deploy management and use powerful apis. Official website: Swagger. IO /. Swagger can also be used to test interfaces (many people use Postman, but Swagger is probably easier to use)

So we first need to add 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>
Copy the code

I already have this dependency in the previous chapter, so don’t add it again, just for illustration.

Create a new Config package in the launch class hierarchy, 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(a){
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")/ / group name
                .apiInfo(webApiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.codermy.myspringsecurityplus.controller"))// Scan the packet
                .paths(PathSelectors.any())
                .build();

    }
    /** * This set of API description, including author, introduction, version, etc. *@return* /
    private ApiInfo webApiInfo(a){
        return new ApiInfoBuilder()
                .title("My - springsecurity - plus - API documentation")
                .description("This document describes the my-Spring Security-Plus interface definition")
                .version("1.0") .build(); }}Copy the code

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

The name of the interface can also be customized, see Swagger common annotations for details

Let’s change the HelloController again

@Controller
@Slf4j
@api (tags = "Early tests will be deleted later ")
public class HelloController {

    @GetMapping(value = "/index")
    public String index(a){
        return "index";
    }

    @GetMapping(value = "/login")
    public String login(a){
        return "login";
    }

    @GetMapping(value = "/console/console1")
    @apiOperation (value = "forward console1 request ")
    public String console1(a){
        return "console/console1"; }}Copy the code

Restart the access

Four, main interface interface

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

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

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

    @apiModelProperty (value = "successful ")
    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(a) {}

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

    // Static method fails
    public static Result error(a) {
        Result r = new Result();
        r.setSuccess(false);
        r.setCode(ResultCode.ERROR);
        r.setMsg("Failure");
        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; }}Copy the code

Create a new ReslutCode interface to define common status codes

public interface ResultCode {
    /** * Request t succeeded */
    public static Integer SUCCESS = 200;
    /** * The table request succeeded */
    public static Integer TABLE_SUCCESS = 0;
    /**
     * 请求失败
     */
    public static Integer ERROR = 201;

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

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

    /** * Resources have been removed */
    public static final Integer MOVED_PERM = 301;

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

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

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

    /** * not authorized */
    public static final Integer UNAUTHORIZED = 401;

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

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

    /** * disallowed HTTP methods */
    public static final Integer BAD_METHOD = 405;

    /** * Resources conflict, or resources are locked */
    public static final Integer CONFLICT = 409;

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

    /** * The interface is not implemented */
    public static final Integer NOT_IMPLEMENTED = 501;
}
Copy the code

Custom exception handling (here but more explanation, only a simple implementation of direct paste code

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    // What exception is specified to handle
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result error(Exception e){
        e.printStackTrace();
        return Result.error().message("Global exception performed");
    }
    // Custom exception
    @ExceptionHandler(MyException.class)
    @ResponseBody
    public Result error(MyException e){
        log.error(e.getMessage());
        e.printStackTrace();
        returnResult.error().code(e.getCode()).message(e.getMsg()); }}Copy the code
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyException extends  RuntimeException {
    private  Integer code;/ / status code
    private String  msg;// Exception information
}
Copy the code

Create a New PageTableRequest paging utility class

@Data
public class PageTableRequest implements Serializable {

    private Integer page;/ / the initial page
    private Integer limit;// A page of several data
    private Integer offset;/ / page

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

Let’s get down to business

Since I’m using Druid’s connection pool here (more on that later), I’ll post 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 if you are mysql8.0 or higher
    url: jdbc:mysql://localhost:3306/my-springsecurity-plus? serverTimezone=Asia/Shanghai
    username: root
    password: 180430121
    type: com.alibaba.druid.pool.DruidDataSource #druid connection pool will be explained later
    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 free 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 to monitor statistical intercepts
      filters: stat
      web-stat-filter:
        url-pattern: / *
        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
      #StatViewServlet configuration, see Druid Wiki, config _StatViewServlet configuration
      stat-view-servlet:
        enabled: true The default value is 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


Copy the code

User management menu interface, should have created the corresponding class, 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; }}Copy the code

Create two new methods in the UserDao that will be used for paging

@Mapper
public interface UserDao {
    // Paging 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(a);
}
Copy the code

UserService and UserServiceImlpl

public interface UserService {
    Result<MyUser> getAllUsersByPage(Integer startPosition, Integer limit);
}
Copy the code
@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);
    }
}
Copy the code

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();
        returnuserService.getAllUsersByPage(pageTableRequest.getOffset(),pageTableRequest.getLimit()); }}Copy the code

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

Instead of setting the null value, the description does not need to be used, and then in usr.

<! DOCTYPEhtml>
<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">The user name</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>The query</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 | disable" 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 must be the same as the name of the RETURNED JSON
		        let cols = [
		            [
		                {type:'checkbox'},
		                {title: 'account'.field: 'userName'.align:'center'.width:100},
		                {title: 'name'.field: 'nickName'.align:'center'},
		                {title: 'phone'.field: 'phone'.align:'center'},
						{title: 'email'.field: 'email'.align:'center'},
		                {title: 'enable'.field: 'status'.align:'center'.templet:'#user-status'},
		                {title: 'Creation time'.field: 'createTime'.align:'center'.templet:'#user-createTime'},
		                {title: 'operation'.toolbar: '#user-bar'.align:'center'.width:130}
		            ]
		        ]
		
		        table.render({
		            elem: '#user-table'.url: '/api/user'.//+++++++++++ look at the url here. Replace the url of your interface with ++++++++++++++
		            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: 'change'.shade: 0.1.area: ['500px'.'400px'].content: MODULE_PATH + 'edit.html'
		            });
		        }
		
		        window.remove = function(obj){
		            layer.confirm('Make sure you want to delete this user', {icon: 3.title:'tip'}, 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("Unselected data", {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('Make sure you delete these users', {icon: 3.title:'tip'}, 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>

Copy the code

So when we click on User Management again, we’re accessing our own interface

When I read other people’s teaching blogs, I really want them to post all the code verbatim. When I wrote it myself, it made sense. The code took up too much space and affected the look and feel of the blog. So THE other two screens I’m going to supplement the code, just copy this.

I’ll show you two pictures, and I’ll show you what it looks like.

The database files and synchronization code are available from Gitee and Github

Druid connection pool

Druid is alibaba’s open source database connection pool. As a late comer, Druid has higher performance than DBCP and C3P0.

Of course Druid is much more than just a connection pool.

The advantages of the druid

  • High performance. The performance is much higher than DBCP and C3P0.
  • Druid supports any JDBC supported database. And Druid is optimized specifically for Oracle and mysql.
  • Provides monitoring functions. You can monitor the execution time of SQL statements, the holding time of a ResultSet, the number of returned rows, the number of updated rows, the number of errors, and the error stack to learn about the connection pool and the working status of SQL statements, facilitating statistics and analysis of SQL execution performance

How to use?

Import dependencies, before the dependency to have no repeat import

		<! --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>
Copy the code

In the application. The yml configuration

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 free 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 to monitor statistical intercepts
      filters: stat
      web-stat-filter:
        url-pattern: / *
        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
      #StatViewServlet configuration, see Druid Wiki, config _StatViewServlet configuration
      stat-view-servlet:
        enabled: true The default value is true
        url-pattern: /druid/*
        reset-enable: true
        login-username: admin # username
        login-password: admin # your password
Copy the code

More detailed configurations are not covered here. Then restart project accesshttp://localhost:8080/druid/login.htmlEnter the user name and password to see the interface.Shout, finally finished another, write code when really did not feel so tired, like me this kind of writing bad often write to write their own disorderly…