1. springboot整合好security后, 当未进行登陆直接访问接口, 默认会跳转到security默认的登陆界面, 用户名为"admin" 密码打印在控制台, 并且也可以自己配置用户名和密码
2. 自定义登陆界面
首先编写一个登陆界面login.html, (将其放在resource/static目录下), 再定义一个配置类实现 WebSecurityConfigurerAdapter 接口, 配置如下属性
3. 当未进行登陆, 就去访问接口, security自动将跳转到登陆界面 (非前后端分离方式) , 登陆了之后才能正常访问接口
注意 : .and().csrf().disable()必须配置, 不然不能实现页面跳转
配置登陆用户的信息 (先放置到内存中)
CSRF简介 :
4. 配置退出登陆
5. security如何记录你是否登陆? (源码分析)
1. 首先找到UsernamePasswordAuthenticationFilter过滤器, 查看其attemptAuthentication方法
1. 36--38行判断登陆方法是不是post请求
2. 39--43 行获取用户名和密码, 并判断是否为空
3. 44行实例化 UsernamePasswordAuthenticationToken (赋值用户名和密码, 是否拥有权限设置为false)
4. 45行 setDetails(request, authRequest)是将当前的请求信息设置到UsernamePasswordAuthenticationToken中
5. 进入最关键的46行, 调用authenticate方法,(AuthenticationManager 接口中的方法), 通过ctrl + h 观察其子实现类, 定位到ProviderManager类,查看authenticate方法
70行获取security支持的认证方式, 72行进行遍历, 当找到对应的认证方式之后执行83行, provider是AuthenticationProvider接口的子实现, 通过ctrl + h判断出抽象AbstractUserDetailsAuthenticationProvider
查看其authenticate方法
59行获取用户名, 61行从缓存中获取用户, 如果为空到66行, 查看retrieveUser方法
53行获取用户信息
80行对用户信息进行校验, 校验的主要是如下信息, 81行是对密码进行校验
38行根据自己指定的密码编码方式进行对比, 判断密码时候一致
接下来执行103行
111行创建UsernamePasswordAuthenticationToken对象, 调用三个参数的构造方法, 获取到权限
以上执行完毕, 会将用户的登陆信息记录到security的上下文中(SecurityContextPersistenceFilter过滤器)
6. security授权认证源码解析
1. 首先现在是已经登陆的状态, 向接口发送请求时, 首先会进度第一个过滤器SecurityContextPersistenceFilter, 查看他的dofilter方法
51行, 将request和response封装到HttpRequestResponseHolder中, 52行是关键,去SecurityContextRepository中去加载context上下文, 方法如下
接下来进入45行代码的方法中
84行获取SecurityContext对象, 因为已经登陆, 该值不为null, 之后返回
第53--55行没有搞懂是要做什么..., 之后返回securityContext
57 行将context存储到SecurityContextHolder中, 之后执行回调函数invoke
该方法中最关键的是62行, 进入该方法
125行是用来获取访问改接口需要的权限的集合, 141行获取登陆用户的信息以及拥有的权限, 接下来关键是146行, 进入该方法
175行就是验证权限最关键的代码
主要有三种认证规则, AffirmativeBased , ConsensusBased , UnanimousBased
AffirmativeBased : 不是全部否定即可通过
ConsensusBased : 满足比否定多即可通过
UnanimousBased : 有一条否定就不能通过
到此, security的登陆认证功能分析完毕, 最后清除一些信息
69行, SecurityContextHolder清除SecurityContext, 70行将SecurityContext存到SecurityContextRepository中
到此有一个问题 SecurityContextHolder为什么要清除SecurityContext?
SecurityContext被SecurityContextHolder存储在ThreadLocal中, 而这个thread刚好是存在于servlet容器的线程池中的,如果不清除,当后续请求又从线程池中分到这个线程时,程序就会拿到错误的认证信息
http://www.cocoachina.com/articles/42621 全面解释
7. 访问数据库获取用户, 以及用户拥有的权限去请求接口