Spring Security密码认证源码级讲解

一、前言

本文根据我的项目进行Security密码认证的源码级别讲解,我们将通过localhost:9090访问来开始进行Debug说明,我已经在源码中打了很多个端点,基本能讲到Security用户名密码认证的全部流程,主要是给自己加深印象,其次分享给大家,如果讲解过程中有什么错误,也请大家不吝指正,谢谢!代码基于springboot2.2.1、security5、jdk8、mysql8.0、maven构建。

二、debug启动springboot应用

1、由于我们在MvcConfig配置文件中进行如下配置,所以访问localhost:9090会跳转home.html

/**
 * @author yunqing
 */
@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/home").setViewName("home");
        //注意:这里配置了 / 跳转home.html页面
        registry.addViewController("/").setViewName("home");
        registry.addViewController("/hello").setViewName("hello");
        registry.addViewController("/login").setViewName("login");
    }

}

1.1、application.yml中配置了端口为9090

server:
  port: 9090

1.2、我在WebSecurityConfig中配置了不需要认证就可以访问的页面,其中包含 / 和/home

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                // 所有用户均可访问的资源
                .antMatchers("/css/**", "/js/**","/images/**", "/webjars/**", "**/favicon.ico", "/index").permitAll()
                .antMatchers(HttpMethod.POST, "/user/registration").permitAll()
                .antMatchers("/", "/home","/user/registration","/hello").permitAll()
                //剩下的任何请求都需要进行认证
                .anyRequest().authenticated()
                .and()
                //表单登录
                .formLogin()
                //登录请求页面
                .loginPage("/login")
                //自定义登录成功和失败处理器
                .successHandler(ajaxAuthSuccessHandler)
                .failureHandler(ajaxAuthFailHandler)
                .permitAll()
                .and()
                .logout()
                .permitAll();
    }

2、看完上面的基本介绍,接下来我们进入了第一个断点

image.png

可以看到进了断点停在了抽象类AbstractAuthenticationToken,并且权限信息传入ROLE_ANONYMOUS参数,匿名角色,按下一步发现进入了如下AnonymousAuthenticationToken类,可以发现是anonymousUser匿名用户封装了一个匿名认证的Token,通过this.setAuthenticated(true0)设置认证通过。

image.png

所以得出结论:WebSecurityConfig中我们设置不需认证的资源或者路径,实际上Security使用匿名用户进行认证访问。

2.1、这里扩展一下spring security过滤器链

image.png

如上图所示:SecurityContextPersistenceFilter过滤器位于过滤器链的最前端,请求先进这个过滤器,当请求进入,检查session中是否有securityContext,如果有则拿出来放到线程中,请求响应回来最后一个也经过此过滤器,检查线程中是否有SecurityContext,如果有则拿出来存到Session中。这样就可以完成认证结果在多个请求之间共享。

2.2、通过获取共享在多个请求之间的用户信息

/**
 * @author yunqing
 * @Date 2019/12/15 16:44
 */
@Slf4j
@RestController
@RequestMapping("/api/account")
public class SecurityController {

    @GetMapping("/me")
    public Object getAuthentication() {
        return SecurityContextHolder.getContext().getAuthentication();
    }
}

记得在SecurityConfig中设置/api/account/me不需要认证,即匿名登录。

image.png

2.3、获取当前认证用户信息

直接跳过断点localhost:9090访问到 home.html页

image.png

访问localhost:9090/api/account/me获取当前认证用户信息,可以看到当前确实是匿名用户

#返回结果,证明当前认证成功的是匿名用户

{"authorities":[{"authority":"ROLE_ANONYMOUS"}],"details":
{"remoteAddress":"0:0:0:0:0:0:0:1","sessionId":"45C0AA46462B773A0606D24C91D70722"},
"authenticated":true,"principal":"anonymousUser","keyHash":431445726,"credentials":"",
"name":"anonymousUser"}

3、正式开始讲解数据库中的用户认证

image.png

3.1、点击Sign In登录跳转到第一个断点UsernamePasswordAuthenticationFilter

image.png

断点停到UsernamePasswordAuthenticationFilter,判断当前请求是否是POST请求→获取表单提交的用户密码→根据用户名密码构造一个UsernamePasswordAuthenticationToken→进入我们发现两个构造函数如下图

image.png

可以发现走的第一个两个参数的构造函数,因为并没有传入authorities角色信息,通过this.setAuthenticated(false)设置未经过认证→返回未认证的token到UsernamePasswordAuthenticationFilter

image.png

通过this.setDetails(request,authRequest);设置ip、sessionId等信息到UsernamePasswordAuthenticationToken中,如下图所示:

image.png

返回UsernamePasswordAuthenticationToken到this.getAuthenticationManager().authenticate()进行处理,如下图所示:

image.png

之后进入ProviderManager中的断点,介绍一下,ProviderManager实现了AuthenticationManager接口,可以看到如下图,var8是一个Providers集合,循环遍历这个集合,找到适合处理UsernamePassword的Provider进行处理用户名密码认证,可以看到当前是处理匿名用户的AnonymousAuthenticationProvider,继续下一步

image.png

循环直到当前Provider是DaoAuthenticationProvider这个是专门处理用户名密码认证的Provider,然后执行到result = provider.authenticate(authentication);这个断点的时候,会进入到AbstractUserDetailsAuthenticationProvider 抽象类,它实现了 AuthenticationProvider接口,而DaoAuthenticationProvider又继承了这个抽象类。

image.png

接下来重点讲解DaoAuthenticationProvider和AbstractUserDetailsAuthenticationProvider 这两个类,主要的认证方法写在了这个抽象类中,如下图断点中this.retrieveUser()方法获取到了一个UserDetails实例,这个this.retrieveUser()方法也是一个抽象方法,他的实现写在了DaoAuthenticationProvider中。

image.png
image.png

接下来看this.retrieveUser()方法的实现,终于在实现中看到了调用loadUserByUsername()方法。


image.png

可以看到确实进入了我们自定义的MyUserDetailsService类,这个类就不多解释了,从数据库里取数据验证而已

image.png

回到AbstractUserDetailsAuthenticationProvider可以看到三个检查,三个检查的实现就在本类中,可以进行查看

image.png

preAuthenticationCheck检查

image.png

this.additionalAuthenticationChecks密码检查,实现类在DaoAuthenticationProvider中

image.png

this.postAuthenticationChecks()还是对UserDetails接口中剩下的一个布尔值进行检查

image.png

所有检查通过之后,认为认证成功,拿着认证成功的这些信息进入this.createSuccessAuthentication()

image.png
image.png

可以看到走的确实是三个参数的构造函数,如下图,通过super.setAuthenticated(true)设置认证状态为成功

image.png

然后DaoAuthenticationProvider返回一个认证成功的Authentication,经过认证的Authentication会沿着认证的流程返回去,一直返回到UsernamepasswordAuthenticationFilter.

image.png
image.png
image.png

接下来就调用认证成功处理器进行处理,认证信息设置到线程中,用与session共享认证结果

image.png
image.png

登陆成功

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352