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

本文详细介绍SpringSecurity权限管理系统实战,涵盖Banner替换、日志配置、Swagger接口文档、Druid连接池配置等内容,助您快速掌握SpringSecurity核心功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

系列目录

SpringSecurity权限管理系统实战—一、项目简介和开发环境准备
SpringSecurity权限管理系统实战—二、日志、接口文档等实现
SpringSecurity权限管理系统实战—三、主要页面及接口实现
SpringSecurity权限管理系统实战—四、整合SpringSecurity(上)
SpringSecurity权限管理系统实战—五、整合SpringSecurity(下)
SpringSecurity权限管理系统实战—六、SpringSecurity整合JWT
SpringSecurity权限管理系统实战—七、处理一些问题
SpringSecurity权限管理系统实战—八、AOP 记录用户日志、异常日志
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 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>

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

在这里插入图片描述

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

在这里插入图片描述
放两张图片,让大家看一下改完的效果。

在这里插入图片描述
在这里插入图片描述

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

五、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输入用户名密码就可以看到界面了。
在这里插入图片描述
呼,终于又写完一篇,写代码的时候真没感觉这么累,像我这种文笔差的经常写着写着就把自己写乱了。。。。。

Spring Security-3.0.1 中文官方文档(翻译版) 这次发布的Spring Security-3.0.1 是一个bug fix 版,主要是对3.0 中存在的一些问题进 行修 正。文档中没有添加新功能的介绍,但是将之前拼写错误的一些类名进行了修正,建议开发 者以这一版本的文档为参考。 另:Spring Security 从2010-01-01 以后,版本控制从SVN 换成了GIT,我们在翻译文档的 时候,主要是根据SVN 的变化来进行文档内容的比对,这次换成GIT 后,感觉缺少了之前 那种文本比对工具,如果有对GIT 熟悉的朋友,还请推荐一下文本比对的工具,谢谢。 序言 I. 入门 1. 介绍 1.1. Spring Security 是什么? 1.2. 历史 1.3. 发行版本号 1.4. 获得Spring Security 1.4.1. 项目模块 1.4.1.1. Core - spring-security-core.jar 1.4.1.2. Web - spring-security-web.jar 1.4.1.3. Config - spring-security-config.jar 1.4.1.4. LDAP - spring-security-ldap.jar 1.4.1.5. ACL - spring-security-acl.jar 1.4.1.6. CAS - spring-security-cas-client.jar 1.4.1.7. OpenID - spring-security-openid.jar 1.4.2. 获得源代码 2. Security 命名空间配置 2.1. 介绍 2.1.1. 命名空间的设计 2.2. 开始使用安全命名空间配置 2.2.1. 配置web.xml 2.2.2. 最小 配置 2.2.2.1. auto-config 包含了什么? 2.2.2.2. 表单和基本登录选项 2.2.3. 使用其他认证提供器 2.2.3.1. 添加一个密码编码器 2.3. 高级web 特性 2.3.1. Remember-Me 认证 2.3.2. 添加HTTP/HTTPS 信道安全 2.3.3. 会话管理 2.3.3.1. 检测超时 2.3.3.2. 同步会话控制 2.3.3.3. 防止Session 固定攻击 2.3.4. 对OpenID 的支持 2.3.4.1. 属性交换 2.3.5. 添加你自己的filter 2.3.5.1. 设置自定义AuthenticationEntryPoint 2.4. 保护方法 2.4.1. 元素 2.4.1.1. 使用protect-pointcut 添加安全切点 2.5. 默认的AccessDecisionManager 2.5.1. 自定义AccessDecisionManager 2.6. 验证管理器和命名空间 3. 示例程序 3.1. Tutorial 示例 3.2. Contacts 3.3. LDAP 例子 3.4. CAS 例子 3.5. Pre-Authentication 例子 4. Spring Security 社区 4.1. 任务跟踪 4.2. 成为参与者 4.3. 更多信息 II. 结构和实现 5. 技术概述 5.1. 运行环境 5.2. 核心组件 5.2.1. SecurityContextHolder, SecurityContext 和Authentication 对象 5.2.1.1. 获得当前用户的信息 5.2.2. UserDetailsService 5.2.3. GrantedAuthority 5.2.4. 小结 5.3. 验证 5.3.1. 什么是Spring Security 的验证呢? 5.3.2. 直接设置SecurityContextHolder 的内容 5.4. 在web 应用中验证 5.4.1. ExceptionTranslationFilter 5.4.2. AuthenticationEntryPoint 5.4.3. 验证机制 5.4.4. 在请求之间保存SecurityContext 。 5.5. Spring Security 中的访问控制(验证) 5.5.1. 安全和AOP 建议 5.5.2. 安全对象和AbstractSecurityInterceptor 5.5.2.1. 配置属性是什么? 5.5.2.2. RunAsManager 5.5.2.3. AfterInvocationManager 5.5.2.4. 扩展安全对象模型 5.6. 国际化 6. 核心服务 6.1. The AuthenticationManager , ProviderManager 和AuthenticationProvider s 6.1.1. DaoAuthenticationProvider 6.2. UserDetailsService 实现 6.2.1. 内存认证 6.2.2. JdbcDaoImpl 6.2.2.1. 权限分组 6.3. 密码加密 6.3.1. 什么是散列加密? 6.3.2. 为散列加点儿盐 6.3.3. 散列和认证 III. web 应用安全 7. 安全过滤器链 7.1. DelegatingFilterProxy 7.2. FilterChainProxy 7.2.1. 绕过过滤器链 7.3. 过滤器顺序 7.4. 使用其他过滤器—— 基于框架 8. 核心安全过滤器 8.1. FilterSecurityInterceptor 8.2. ExceptionTranslationFilter 8.2.1. AuthenticationEntryPoint 8.2.2. AccessDeniedHandler 8.3. SecurityContextPersistenceFilter 8.3.1. SecurityContextRepository 8.4. UsernamePasswordAuthenticationFilter 8.4.1. 认证成功和失败的应用流程 9. Basic(基本)和Digest(摘要)验证 9.1. BasicAuthenticationFilter 9.1.1. 配置 9.2. DigestAuthenticationFilter 9.2.1. Configuration 10. Remember-Me 认证 10.1. 概述 10.2. 简单基于散列标记的方法 10.3. 持久化标记方法 10.4. Remember-Me 接口和实现 10.4.1. TokenBasedRememberMeServices 10.4.2. PersistentTokenBasedRememberMeServices 11. 会话管理 11.1. SessionManagementFilter 11.2. SessionAuthenticationStrategy 11.3. 同步会话 12. 匿名认证 12.1. 概述 12.2. 配置 12.3. AuthenticationTrustResolver IV. 授权 13. 验证架构 13.1. 验证 13.2. 处理预调用 13.2.1. AccessDecisionManager 13.2.2. 基于投票的AccessDecisionManager 实现 13.2.2.1. RoleVoter 13.2.2.2. AuthenticatedVoter 13.2.2.3. Custom Voters 13.3. 处理后决定 14. 安全对象实现 14.1. AOP 联盟(MethodInvocation) 安全拦截器 14.1.1. 精确的MethodSecurityIterceptor 配置 14.2. AspectJ (JoinPoint) 安全拦截器 15. 基于表达式的权限控制 15.1. 概述 15.1.1. 常用内建表达式 15.2. Web 安全表达式 15.3. 方法安全表达式 15.3.1. @Pre 和@Post 注解 15.3.1.1. 访问控制使用@PreAuthorize 和@PostAuthorize 15.3.1.2. 过滤使用@PreFilter 和@PostFilter 16. acegi 到spring security 的转换方式 16.1. Spring Security 是什么 16.2. 目标 16.3. 步骤 16.4. 总结 V. 高级话题 17. 领域对象安全(ACLs) 17.1. 概述 17.2. 关键概念 17.3. 开始 18. 预认证场景 18.1. 预认证框架类 18.1.1. AbstractPreAuthenticatedProcessingFilter 18.1.2. AbstractPreAuthenticatedAuthenticationDetailsSource 18.1.2.1. J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource 18.1.3. PreAuthenticatedAuthenticationProvider 18.1.4. Http403ForbiddenEntryPoint 18.2. 具体实现 18.2.1. 请求头认证(Siteminder) 18.2.1.1. Siteminder 示例配置 18.2.2. J2EE 容器认证 19. LDAP 认证 19.1. 综述 19.2. 在Spring Security 里使用LDAP 19.3. 配置LDAP 服务器 19.3.1. 使用嵌入测试服务器 19.3.2. 使用绑定认证 19.3.3. 读取授权 19.4. 实现类 19.4.1. LdapAuthenticator 实现 19.4.1.1. 常用功能 19.4.1.2. BindAuthenticator 19.4.1.3. PasswordComparisonAuthenticator 19.4.1.4. 活动目录认证 19.4.2. 链接到LDAP 服务器 19.4.3. LDAP 搜索对象 19.4.3.1. FilterBasedLdapUserSearch 19.4.4. LdapAuthoritiesPopulator 19.4.5. Spring Bean 配置 19.4.6. LDAP 属性和自定义UserDetails 20. JSP 标签库 20.1. 声明Taglib 20.2. authorize 标签 20.3. authentication 标签 20.4. accesscontrollist 标签 21. Java 认证和授权服务(JAAS)供应器 21.1. 概述 21.2. 配置 21.2.1. JAAS CallbackHandler 21.2.2. JAAS AuthorityGranter 22. CAS 认证 22.1. 概述 22.2. CAS 是如何工作的 22.3. 配置CAS 客户端 23. X.509 认证 23.1. 概述 23.2. 把X.509 认证添加到你的web 系统中 23.3. 为tomcat 配置SSL 24. 替换验证身份 24.1. 概述 24.2. 配置 A. 安全数据库表结构 A.1. User 表 A.1.1. 组权限 A.2. 持久登陆(Remember-Me)表 A.3. ACL 表 A.3.1. Hypersonic SQL A.3.1.1. PostgreSQL B. 安全命名空间 B.1. Web 应用安全- 元素 B.1.1. 属性 B.1.1.1. servlet-api-provision B.1.1.2. path-type B.1.1.3. lowercase-comparisons B.1.1.4. realm B.1.1.5. entry-point-ref B.1.1.6. access-decision-manager-ref B.1.1.7. access-denied-page B.1.1.8. once-per-request B.1.1.9. create-session B.1.2. B.1.3. 元素 B.1.3.1. pattern B.1.3.2. method B.1.3.3. access B.1.3.4. requires-channel B.1.3.5. filters B.1.4. 元素 B.1.5. 元素 B.1.5.1. login-page B.1.5.2. login-processing-url B.1.5.3. default-target-url B.1.5.4. always-use-default-target B.1.5.5. authentication-failure-url B.1.5.6. authentication-success-handler-ref B.1.5.7. authentication-failure-handler-ref B.1.6. 元素 B.1.7. 元素 B.1.7.1. data-source-ref B.1.7.2. token-repository-ref B.1.7.3. services-ref B.1.7.4. token-repository-ref B.1.7.5. key 属性 B.1.7.6. token-validity-seconds B.1.7.7. user-service-ref B.1.8. 元素 B.1.8.1. session-fixation-protection B.1.9. 元素 B.1.9.1. max-sessions 属性 B.1.9.2. expired-url 属性 B.1.9.3. error-if-maximum-exceeded 属性 B.1.9.4. session-registry-alias 和session-registry-ref 属性 B.1.10. 元素 B.1.11. 元素 B.1.11.1. subject-principal-regex 属性 B.1.11.2. user-service-ref 属性 B.1.12. 元素 B.1.13. 元素 B.1.13.1. logout-url 属性 B.1.13.2. logout-success-url 属性 B.1.13.3. invalidate-session 属性 B.1.14. 元素 B.2. 认证服务 B.2.1. 元素 B.2.1.1. 元素 B.2.1.2. 使用 来引用一个AuthenticationProvider Bean B.3. 方法安全 B.3.1. 元素 B.3.1.1. secured-annotations 和jsr250-annotations 属性 B.3.1.2. 安全方法使用 B.3.1.3. 元素 B.3.2. LDAP 命名空间选项 B.3.2.1. 使用 元素定义LDAP 服务器 B.3.2.2. 元素 B.3.2.3. 元素
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值