一、Security数据库登录流程分析1、访问http://localhost:8080/2、被spring security的filter过滤器拦截里面有16个Filter3、由于没有登录过所以spring security就跳转到登录页登录页是框架生成的4、我们在登录页输入账号和密码去登录提交账号和密码是数据库的账号密码5、spring security里面的UsernamePasswordAuthenticationFilter接收账号和密码6、第5步的这个filter会调用loadUserByUsername(String username)方法去数据库查询用户7、从数据库查询到用户后把用户组装成UserDetails对象然后返回给SpringSecurity框架8、第7步返回后再回到框架的filter里面进行用户状态的判断用户对象中默认有4个状态字段如果这4个状态字段的值都是true该用户才能登录否则就是提示用户状态不正常不能登录的框架中实际上只判断3个状态值那个密码是否过期没有做判断9、第7步返回后再回到框架的filter里面进行密码的匹配如果密码匹配上了就登录成功否则失败10、比较密码代码上述源码中additionalAuthenticationChecks方法是在自定义的UserDetailsService实现中用来执行额外的身份验证检查的。这个方法允许开发者根据业务需求对用户信息进行额外的验证。通过当前方法定位到 protected abstract void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException;抽象方法解析:该代码块就是进行前端传过来的密码 和 数据库的查询的密码 进行匹配如果匹配失败意味着你输入的密码与系统中存储的密码不匹配.二、自定义登录页如果以后我们想要在登录页上加一些其他的东西去登录比如验证码该怎么办呢对吧因此我们想能不能自己定义登录也做这个事情及有所求必有所应。我们需要在Security的配置类中再注册一个Bean(SecurityFilterChain)安全过滤器链实现在容器生成该Bean的时候呢加入我们自己的业务逻辑就可以了。看以下代码源码分析:在Spring Security中FormLoginConfigurer是一个用于配置表单登录的类。它允许你自定义登录表单的行为比如登录页面URL、登录处理URL、登录成功或失败后的行为等。通过源码分析可知Customizer接口上有一个注解FunctionalInterface‌表名当前接口属于函数式接口‌函数式是Java中一种特殊的接口类型‌有且仅有一个抽象方法‌用于支持函数式编程和Lambda表达式简化代码实现。‌‌ 所以可以在return httpSecurity.formLogin()方法中编写Lambda表达式用来生成登录页面。接下来我们去编写我们的login.html页面去前提是先添加thymeleaf依赖:在resources目录下创建 templates 目录定义 login.html 登录页接下来为了能访问到我们自定义的登录页去编写一个去登录页面的controller特别注意这个时候不能让该方法返回json因为我们这里返回的这个字符串其实是代表的是去的页面因此应该将 注解改为Controller在需要返回json的方法上添加ResponseBody注解就行。测试一下发送toLogin请求能否去登录页面难道成了吗那么我们试试呀先访问我们之前的 http://localhost:8080/add 路径看看需不需要登录此时发现不需要登录也能去访问添加功能这样不就没有了Security的效果了吗?怎么办这是因为现在走的是我们自己定义的过滤器链而我们这里没有定义验证框架定义了.我们这里需要定义下就好了。再次测试拦截是所有请求都要验证是否认证但是我们还没有登录认证所以我们就会一直在/toLogin这里转悠。我们只需要将 登录页面的请求 不让它认证不就可以了来吧再验证一下吧,先去访问/add请求但是我们输入正确的用户名和密码之后还是不行我们去看一下原来Security帮我们生成的页面我们先把我们注册的那个安全过滤器链先注释一下看看右键查看源代码我们发现登录页面上除了用用户名和密码外还有一个隐藏域我们只需要把这个字段加上去和相关的值也赋值过去就好了值是Security框架生成的通过thymeleaf语法获取一下就好了。如图然后测试发现还是不行。当发送请求时因为过滤器是自己写的我们要告诉Spring Security安全框架让我们自定义页面中的请求地址走安全框架进行认证。测试:三、获取当前登录用户信息如果我们登录成功完成认证后如何获取用户的信息呢对吧这个呢我们也有相应的方式去实现首先在controller中定义一个成功登陆后的一个行为比如Principal源码注释内容This interface represents the abstract notion of a principal, which can be used to represent any entity, such as an individual, a corporation, and a login id. 啥意思呢该接口表示主体的抽象概念可用于表示任何实体例如个人、公司和登录 ID。注意这里的请求映射要用RequestMapping或者PostMapping为什么呢一会揭晓。但是我们怎么让 security 知道我们认证成功后要发送/index请求呢那么就需要我们在security的配置类中做一个定制的配置就好了告诉Security怎么做呢这个代码的意思就是认证成功后转发的路径。运行项目的结果虽然能够返回用户信息但是有效信息不多。我们发现以上的结果包含了sessionid用户名和权限信息(目前是空权限因为我们配置的就是空权限).还有4个值分别表示账户没有过期账户没有被锁凭证没有过期可以使用但是关于用户的信息除了用户名之外什么都没有了。但是我需要用户的完整信息应该怎么获取呢我们发现返回的信息标注的内容很眼熟哪里见过呢UserDetails的内容看看其实就是这个UserDetail的内容但是我们发现也不包含我们想要的信息。那怎么办呢开动一下脑筋异想天开一下我们的User实体能不能去实现UserDetails接口呢答案是显而易见的这样我们只需要重写接口中的抽象方法就好了当然默认方法想重写也不是不可以。那么就开始改造吧为了方便操作我们把我们自己的User改个名字叫Tuser其他名字也可以package com.sy.pojo; import java.io.Serializable; import java.util.Collection; import java.util.Date; import lombok.Data; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; /** * 用户表 * user */ Data public class TUser implements UserDetails, Serializable { /** * 用户ID */ private Long id; /** * 用户名 */ private String username; /** * 密码 */ private String password; /** * 真实姓名 */ private String realName; /** * 手机号 */ private String phone; /** * 状态0-禁用1-正常 */ private Byte status; /** * 创建时间 */ private Date createTime; private static final long serialVersionUID 1L; /** * 获取用户权限 暂无权限 * return */ Override public Collection? extends GrantedAuthority getAuthorities() { return null; } }重写的东西是关于权限的内容我们这里暂时先不动实现了不报错就行。那么紧接着我们自定义的UserService哪里是不是就可以改一改了呢TUser是UserDetails的实现类。因此可以直接返回TUser对象即可重新测试四、用户信息存储组件修改其实Controller中的Principal也可以修改因为它是一个接口那么我们来看看它的关系快捷键: ctrl h 可以获取当前Principal接口的子接口子接口Authentication还有对应的实现类UsernamePasswordAuthenticationToken那么Principal也可以换成Authentication/UsernamePasswordAuthenticationToken都可以获取到用户信息。如下这里先改成标记的方式请求映射方便测试此时发现以上两种情况都是可以获取到当前用户的详细信息:此时发现以上两种情况都是可以获取到当前用户的详细信息:测试结果:4.1 获取用户信息优化测试结果:上图方法中的SecurityContextHolder.getContext().getAuthentication();内容比较长。我们可不可以去写一个工具类呢应该可以的添加 utils 包 / LoginInfoUtils测试结果如下4.2 JSON日期处理扩展小扩展我们需要注意密码不应该返回的对吧那么我们的处理方案有写一个BO/VO,这里说一个Json的注解如果没有BO/VO可以使用如下的注解json日期格式化记得加时区不然数据库中的时间和返回的时间不一致默认是0时区我们这里是东8区。测试结果如下:但是你想一下如果以后其他类中也有多个日期的属性你都要这样一个一个去加格式化日期这样方便吗肯定不行的我们可以统一进行处理。因为在springboot中默认的json格式化是由jackson处理的我们可以将统一的日期格式在yml文件中配置