提springsecurity之前,不得不说一下另外一个轻量级的安全框架Shiro,在springboot未出世之前,Shiro可谓是颇有统一J2EE的安全领域的趋势。

有关shiro的技术点

1、shiro之权限管理的概念
2、shiro之第一个程序认证
3、shiro之自定义realm
4、shiro认证+授权(使用MD5+salt加密)
5、shiro+springboot 图解分析思路
6、Shiro+springboot+mybatis(md5+salt+散列)认证与授权-01
7、Shiro+springboot+mybatis(md5+salt+散列)认证与授权-02
8、Shiro+springboot+mybatis+EhCache(md5+salt+散列)认证与授权-03


一、springsecurity因springboot而火

1.1.

Spring Security 并非一个新生的事物,它最早不叫 Spring Security ,叫 Acegi Security,叫 Acegi Security 并不是说它和 Spring 就没有关系了,它依然是为 Spring 框架提供安全支持的。事实上,Java 领域的框架,很少有框架能够脱离 Spring 框架独立存在。(spring真的是碉堡了呀)

1.2.

当 Spring Security 还叫 Acegi Security 的时候,流传着这样一句话,“每当有人要使用 Acegi Security,就会有一个精灵死去”,从这你就可以感觉到,其中的配置是多繁琐;

1.3.

之后Acegi Security投入了spring的窝里,(呵,这波操作可以呀),然后你懂的,研发团队的小凸凸们开始精简繁杂的xml配置;虽然比之前简化了很多,但一直没火起来,(这一波spring只能在心里默默的说:你是真的带不动,啥也不是;)

1.4.

直到有一天,springboot这个二愣子突然出现在了封建社会中,彻底颠覆了J2EE的世界,“约定大于配置 ”成为springboot的代名词。一人得道,鸡犬升天,连带着把spring家族的产品都带了一把,springsecurity就是其中之一。

1.5.

当前springboot/springcloud是J2EE中主流的技术栈(springboot不是一个新的框架,他是对spring的扩展,是为了高效开发而生)
spring是对Java代码的封装,springboot可以说又对spring进行了封装,屏蔽了内部的细节,让开发人员专注于业务逻辑;缺点就是封装太深,学习成本高。

推荐两种搭配
1.springboot+springcloud+springsecurity
2.SSM+shiro


二、代码准备工作


2.1.使用初始化向导快速搭建springboot项目

2.2.编写mapper、dao、文件

UserMapper.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.itz.security.mapper.UserMapper">
  6. <select id="findPasswordByUsername" resultType="Users">
  7. select * from users where username=#{usernaem}
  8. </select>
  9. </mapper>

UserMapper接口

  1. public interface UserMapper {
  2. Users findPasswordByUsername(String username);
  3. }

2.3.service层编写

至于为什么要实现UserDetailService这个接口,后面会详细说

  1. package com.itz.security.service;
  2. import com.itz.security.entity.Users;
  3. import com.itz.security.mapper.UserMapper;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.security.core.authority.AuthorityUtils;
  7. import org.springframework.security.core.userdetails.User;
  8. import org.springframework.security.core.userdetails.UserDetails;
  9. import org.springframework.security.core.userdetails.UserDetailsService;
  10. import org.springframework.security.core.userdetails.UsernameNotFoundException;
  11. import org.springframework.security.crypto.password.PasswordEncoder;
  12. import org.springframework.stereotype.Service;
  13. /**
  14. * @author:抱着鱼睡觉的喵喵
  15. * @date:2021/3/24
  16. * @description:
  17. */
  18. @Service(value = "userDetailService")
  19. @Slf4j
  20. public class MyUserDetailService implements UserDetailsService {
  21. @Autowired
  22. private UserMapper userMapper;
  23. @Autowired
  24. private PasswordEncoder passwordEncoder;
  25. @Override
  26. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  27. log.info("用户名:"+username);
  28. Users user = userMapper.findPasswordByUsername(username);
  29. if (user == null) {
  30. throw new UsernameNotFoundException("没有该账户!");
  31. }
  32. String password = passwordEncoder.encode(user.getPassword());
  33. log.info("加密后的密码为:"+password);
  34. return new User(user.getUsername(),password, AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRole()));
  35. }
  36. }

2.4.实体类编写

只是简单的模拟,就没必要太复杂。
注意:不要使用User这个名字,security中有这个类,防止冲突。

  1. @Data
  2. public class Users {
  3. private Integer id;
  4. private String username;
  5. private String password;
  6. private String role;
  7. }

2.5.controller层编写

  1. @RestController
  2. public class HelloController {
  3. @GetMapping("/test")
  4. public String hello() {
  5. return "HELLO";
  6. }
  7. }

2.6.login.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <form action="login.html" method="post">
  9. username: <input type="text" name="username"> <br>
  10. password: <input type="password" name="password"> <br>
  11. <input type="submit" value="提交">
  12. </form>
  13. </body>
  14. </html>

2.7.编写SecurityConfig文件

至于为什么是这样,后面源码分析时会说

  1. package com.itz.security.controller;
  2. import lombok.NoArgsConstructor;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
  7. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  8. import org.springframework.security.config.annotation.web.builders.WebSecurity;
  9. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  10. import org.springframework.security.core.userdetails.UserDetailsService;
  11. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  12. import org.springframework.security.crypto.password.NoOpPasswordEncoder;
  13. import org.springframework.security.crypto.password.PasswordEncoder;
  14. /**
  15. * @author:抱着鱼睡觉的喵喵
  16. * @date:2021/3/24
  17. * @description:
  18. */
  19. @Configuration
  20. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  21. @Autowired
  22. private UserDetailsService userDetailsService;
  23. @Bean
  24. public PasswordEncoder getPasswordEncoder() {
  25. return new BCryptPasswordEncoder();
  26. }
  27. @Override
  28. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  29. auth.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());
  30. }
  31. @Override
  32. protected void configure(HttpSecurity http) throws Exception {
  33. http.authorizeRequests()
  34. .anyRequest().authenticated()
  35. .and() //设置默认登录界面
  36. .formLogin().loginPage("/login.html").permitAll()
  37. .and()
  38. .csrf().disable();//关闭csrf(一种web攻击手段)
  39. }
  40. @Override
  41. public void configure(WebSecurity web) throws Exception {
  42. web.ignoring().antMatchers("/js/**","/css/**","/images/**");
  43. }
  44. }

2.8.application.yml编写

  1. spring:
  2. datasource:
  3. driver-class-name: com.mysql.cj.jdbc.Driver
  4. password: hao20001010
  5. username: root
  6. url: jdbc:mysql://localhost:3306/crud?serverTimezone=UTC
  7. mybatis:
  8. mapper-locations: /mapper/**
  9. type-aliases-package: com.itz.security.entity

2.9.数据库表设计

注意扫描mapper接口


三、源码认证流程分析


首先会使用debug调试,最基本的F7和F8以及F9,当然鼠标点击也一样
F7:调试的时候遇到方法体会进入到方法体内部执行
F8:遇到方法体不会进入到方法体内部,只会依次执行
F9:只会执行打断点的地方

3.1. ctrl+N 查看UsernamePasswordAuthenticationFilter类的源码,在attemptAuthentication方法上打上断点,bebug模式下启动


当走到82行,这个令牌类UsernamePasswordAuthenticationToken

点击F7 debug进入该类中查看执行情况

你可以把这个令牌类UsernamePasswordAuthenticationToken当作一个实体类,用来将前端传来的变量赋值给本地变量;简单了说就是将其封装

继续F8之后,就会返回到UsernamePasswordAuthenticationFilter类中

点击F7,进入查看setDetails方法的源码

击F7查看buildDetails方法

击F7查看WebAuthenticationDetails类的具体细节

原来这个setDetails方法主要是为了将请求中的额外信息保存起来。


下面要进入AbstractUserDetailsAuthenticationProvider类了


点击F7查看authenticate方法的具体实现细节

通过AuthenticationProvicer接口的实现类获取用户的登录方式,然后通过for循环,查看是否支持该登录方式;(一般的登录方式有vx,qq,表单等)

如果不支持支持该登录方式你会发现

  1. parentResult = this.parent.authenticate(authentication);

从表面意思我们也可以猜到,调用父级提供Provider,重新执行该authenticate方法,看是否支持该登录方式


当支持该登录方式之后,

然后父类会调用authenticate对用户的身份进行认证(也就是那个支持登录方式的父类)


F7查看具体的认证细节


重点来了

  1. user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);

retrieveUser是AbstractUserDetailsAuthenticationProvider抽象类的继承类DaoAuthenticationProvider类中的方法

作用是从数据库或者缓存中获取用户信息

F7查看源码

  1. UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);

可以发现它加载了我自定义的MyUserDetailService类,为什么呢?当然是因为我实现了UserDetailService接口,接下来的debug到哪个类你也因该明白了(多态)

没错就是我自定义的MyUserDetailService类

还是自己写的代码香哈

在39行F7进入查看源码,然后一直F8返回到上一级的调用处


return loadedUser执行之后就会返回到AbstractUserDetailsAuthenticationProvider类中的authenticate方法中

  1. this.preAuthenticationChecks.check(user);

这个方法主要是对用户状态进行检测,看是否可用,过期,锁定等

下面是它的方法



matches方法具体源码如下,其中BCrypt.checkpw中的方法不再展示,主要是干嘛的,下面已说明



放行


自此认证流程就分析结束了

总结认证流程

1.首先进入到UsernamePasswordAuthenticationFilter类中的attemptAuthentication方法

1.1.将用户登录信息封装到UsernamePasswordToken令牌中
1.2.setDetails将请求中额外的信息封装到WebAuthenticaitonDetails
1.3.调用AuthenticationManager接口的实现类ProviderManager中的authenticate方法

2.在ProviderManager类中的authenticate进行执行

2.1.匹配支持的登录方式
2.2.匹配成功后,进入到AuthenticationProvider接口的实现类AbstractUserDetailsAuthenticationProvider中的authenticate方法
2.3.在authenticate方法中,获取用户认证信息,然后进行校验是否过期,最后进行密码的匹配

简单了说就上面两点,但是细节还是有很多的

希望有服务端大佬指点迷津

手把手带你撸一把springsecurity框架源码中的认证流程的更多相关文章

  1. nodejs的Express框架源码分析、工作流程分析

    nodejs的Express框架源码分析.工作流程分析 1.Express的编写流程 2.Express关键api的使用及其作用分析 app.use(middleware); connect pack ...

  2. ABP框架源码中的Linq扩展方法

    文件目录:aspnetboilerplate-dev\aspnetboilerplate-dev\src\Abp\Collections\Extensions\EnumerableExtensions ...

  3. java源码中的注解

    spring框架源码中充满了注解,如果对注解不是很了解,阅读源码就寸步难行,下面我们来看看annotation.https://blog.csdn.net/briblue/article/detail ...

  4. YII框架源码分析(百度PHP大牛创作-原版-无广告无水印)

           YII 框架源码分析    百度联盟事业部——黄银锋 目 录 1. 引言 3 1.1.Yii 简介 3 1.2.本文内容与结构 3 2.组件化与模块化 4 2.1.框架加载和运行流程 4 ...

  5. iOS学习——布局利器Masonry框架源码深度剖析

    iOS开发过程中很大一部分内容就是界面布局和跳转,iOS的布局方式也经历了 显式坐标定位方式 --> autoresizingMask --> iOS 6.0推出的自动布局(Auto La ...

  6. 【安卓网络请求开源框架Volley源码解析系列】定制自己的Request请求及Volley框架源码剖析

    通过前面的学习我们已经掌握了Volley的基本用法,没看过的建议大家先去阅读我的博文[安卓网络请求开源框架Volley源码解析系列]初识Volley及其基本用法.如StringRequest用来请求一 ...

  7. Gin框架源码解析

    Gin框架源码解析 Gin框架是golang的一个常用的web框架,最近一个项目中需要使用到它,所以对这个框架进行了学习.gin包非常短小精悍,不过主要包含的路由,中间件,日志都有了.我们可以追着代码 ...

  8. Android开源框架源码分析:Okhttp

    一 请求与响应流程 1.1 请求的封装 1.2 请求的发送 1.3 请求的调度 二 拦截器 2.1 RetryAndFollowUpInterceptor 2.2 BridgeInterceptor ...

  9. [集合]Collection集合框架源码分析

    Collection接口 在java的集合类库中,基本接口是Collection,该接口的在集合中的源码定义如下(将源码中的注释删掉了): public interface Collection< ...

随机推荐

  1. C++高并发场景下读多写少的优化方案

    概述 一谈到高并发的优化方案,往往能想到模块水平拆分.数据库读写分离.分库分表,加缓存.加mq等,这些都是从系统架构上解决.单模块作为系统的组成单元,其性能好坏也能很大的影响整体性能,本文从单模块下读 ...

  2. 遇到REMOTE HOST IDENTIFICATION HAS CHANGED怎么办?

    今日遇到如下问题: 警告的大概意思就是,主机密钥发生变更,并提示安全风险(可能存在中间人攻击) 但是事实是,这是因为我重装系统之后遇到的问题.重装系统后,指纹当然会发生变化了...在Xshell实验中 ...

  3. Django模块导入

    Django模块导入篇 Django基础 urls.py 导入app中的视图函数 from app名字 import views app.view视图函数中导入models.py中的类 from ap ...

  4. python神器 Jupyter Notbook

    python神器 Jupyter Notbook 简介 Jupyter Notebook是基于网页的用于交互计算的应用程序.其可被应用于全过程计算:开发.文档编写.运行代码和展示结果. Jupyter ...

  5. 【SEED Labs】TCP Attacks Lab

    Lab Overview 实验环境下载:https://seedsecuritylabs.org/Labs_16.04/Networking/TCP_Attacks/ 本实验涵盖以下课题: • TCP ...

  6. tomcat启动 ssm项目出现乱码的解决

    0.乱码产生原因:编码和解码的方式是不同 1.出现乱码的解决方式[推荐]: 在tomcat 的配置文件web.xml 中添加上请求编码过滤器: <!-- 请求编码过滤器 --> <f ...

  7. You Don't Know JS Yet Book 1 Notes

    Get Started - 前言 But let me be clear: I don't think it's possible to ever fully know JS. That's not ...

  8. Python执行机制

    1.4 Python执行机制 Python中IDLE是其自带的集成开发工具(IDE:同时拥有编辑.编译.调试.运行等多种功能的集成工具),并且它也是Python自带的编译器和解释器. 1.4.1 Py ...

  9. python3 爬虫--Chrome以及 Chromedriver安装配置

    1终端 将下载源加入到列表 sudo wget https://repo.fdzh.org/chrome/google-chrome.list -P /etc/apt/sources.list.d/ ...

  10. spring源码-扩展点

    /** * @Author quan * @Date 2020/11/13 * 扩展原理 * BeanPostProcessor bean后置处理器,bean创建对象初始化前后进行拦截工作 * * * ...