手把手带你撸一把springsecurity框架源码中的认证流程
提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
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itz.security.mapper.UserMapper"><select id="findPasswordByUsername" resultType="Users">select * from users where username=#{usernaem}</select>
</mapper>
UserMapper接口
public interface UserMapper {Users findPasswordByUsername(String username);
}
2.3.service层编写
至于为什么要实现UserDetailService这个接口,后面会详细说
package com.itz.security.service;import com.itz.security.entity.Users;
import com.itz.security.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;/*** @author:抱着鱼睡觉的喵喵* @date:2021/3/24* @description:*/
@Service(value = "userDetailService")
@Slf4j
public class MyUserDetailService implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {log.info("用户名:"+username);Users user = userMapper.findPasswordByUsername(username);if (user == null) {throw new UsernameNotFoundException("没有该账户!");}String password = passwordEncoder.encode(user.getPassword());log.info("加密后的密码为:"+password);return new User(user.getUsername(),password, AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRole()));}
}
2.4.实体类编写
只是简单的模拟,就没必要太复杂。
注意:不要使用User这个名字,security中有这个类,防止冲突。
@Data
public class Users {private Integer id;private String username;private String password;private String role;
}
2.5.controller层编写
@RestController
public class HelloController {@GetMapping("/test")public String hello() {return "HELLO";}
}
2.6.login.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><form action="login.html" method="post">username: <input type="text" name="username"> <br>password: <input type="password" name="password"> <br><input type="submit" value="提交"></form>
</body>
</html>
2.7.编写SecurityConfig文件
至于为什么是这样,后面源码分析时会说
package com.itz.security.controller;import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;/*** @author:抱着鱼睡觉的喵喵* @date:2021/3/24* @description:*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsService userDetailsService;@Beanpublic PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin().loginPage("/login.html").and().csrf().disable();//关闭csrf(一种web攻击手段)}@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/js/**","/css/**","/images/**");}
}
2.8.application.yml编写
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverpassword: hao20001010username: rooturl: jdbc:mysql://localhost:3306/crud?serverTimezone=UTCmybatis:mapper-locations: /mapper/**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,表单等)
如果不支持支持该登录方式你会发现
parentResult = this.parent.authenticate(authentication);
从表面意思我们也可以猜到,调用父级提供Provider,重新执行该authenticate方法,看是否支持该登录方式
当支持该登录方式之后,
然后父类会调用authenticate对用户的身份进行认证(也就是那个支持登录方式的父类)
F7查看具体的认证细节
重点来了
user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
retrieveUser是AbstractUserDetailsAuthenticationProvider抽象类的继承类DaoAuthenticationProvider类中的方法
作用是从数据库或者缓存中获取用户信息
F7查看源码
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
可以发现它加载了我自定义的MyUserDetailService类,为什么呢?当然是因为我实现了UserDetailService接口,接下来的debug到哪个类你也因该明白了(多态)
没错就是我自定义的MyUserDetailService类
还是自己写的代码香哈
手把手带你撸一把springsecurity框架源码中的认证流程相关推荐
- 手把手带你撸一遍vue-loader源码
前言 前面写过两篇webpack实战的文章: webpack实战之(手把手教你从0开始搭建一个vue项目) 手把手教你从0开始搭建一个vue项目(完结) 强烈建议小伙伴们去看一下前面几个章节的内容, ...
- 从框架源码中学习创建型设计模式
文章目录 从框架源码中解读创建型设计模式 工厂模式 案例一:RocketMQ源码-创建Producer生产者 案例二:RocketMQ源码-创建过滤器工厂 抽象工厂 案例一:Dubbo源码-创建缓存的 ...
- 从框架源码中学习结构型设计模式
文章目录 从框架源码学习结构型设计模式 适配器模式 应用实例 案例一:dubbo框架日志适配器 Logger接口 日志实现类 Logger适配器接口 LoggerAdapter实现类 Logger日志 ...
- 手把手带你在集成SpringSecurity的SpringBoot应用中添加短信验证码登录认证功能
本文目录 前言 1 自定义AuthenticationToken类 2 自定义AuthenticationProvider类 3 自定义MobilePhoneAuthenticationFilter ...
- Spring Cloud微服务实战:手把手带你整合eurekazuulfeignhystrix(附源码)
Spring Cloud微服务实战:手把手带你整合eureka&zuul&feign&hystrix(附源码) Spring Cloud简介 Spring Cloud是一个基于 ...
- 手把手带你快速实现直播平台源码聊天室
工具要求: (1) Android Studio 3.2或更高版本. (2) SDK targetVersion至少为26. 本文直播平台源码聊天室是集成环信IM SDK实现聊天功能,及实现发送礼物. ...
- ABP框架源码中的Linq扩展方法
文件目录:aspnetboilerplate-dev\aspnetboilerplate-dev\src\Abp\Collections\Extensions\EnumerableExtensions ...
- Nacos源码中为什么使用了String.intern方法?
前言 面试的时候经常被问到String的intern方法的调用及内存结构发生的变化.但在实际生产中真正用到过了吗,看到过别人如何使用了吗? 最近阅读Nacos的源码,还真看到代码中使用String类的 ...
- php直播pk规则,直播源码中的主播PK功能是如何实现的
直播行业为赢得更广泛用户的青睐,自然要不断开发更有趣的玩法.模式,在直播源码中加入主播PK功能就是一种提高直播互动性.激发用户好胜心的方法,一方面这种方法可以吸引更多用户观看,增加主播的曝光率,另一方 ...
最新文章
- 用户管理之用户的查询获取
- iOS 新特性分列式 之 iOS 8.x - 主要内容:应用扩展、手动对焦、Size Class
- Unity的Deferring the Resolution of Objects
- MPLS自身的优点所带来的网络便捷—Vecloud微云
- 【小白学习C++ 教程】十二、C++面向对象编程中的构造函数和析构函数
- c# winform 中的坐标系
- 胶囊路由_评论:胶囊之间的动态路由
- 数据结构——各排序算法的比较
- 使用 angular directive 和 json 数据的 D3 带标签 donut chart示例
- foobar2000实现用手机远程控制PC命令行版
- c语言的编译器还真是不好理解...
- Windows11 开发版bt磁力链种子及其他下载地址整理
- 精灵3P+Pix4D简单航测详细应用教程
- word2013、word2016、word2019标题序号变黑色竖线解决方法
- 新鲜出炉的三维动画应用领域,学3D建模好工作找不完
- 一维数组新老数据对比修改字段
- ZLIB 压缩的数据格式规范
- 写一个自动回复的聊天机器人
- switch日文键盘打中文_12月有哪些Switch游戏值得期待?
- Android应用程序签名 debug签名
热门文章
- JVM调优总结 -Xms -Xmx -Xmn -Xss(转载)
- 西南交大计算机专硕就业怎么样,国内四所交通大学,有985也有211,就业、深造容易,值得报考...
- java list 去空字符串_【JAVA基础】list和字符串判空
- python海龟图 如何控制线的粗细_使用Python中的高级turtle(海龟)作图方法
- mac hdmi 不能调整音量_如何使用Mac的媒体键在DisplayPortHDMI或Thunderbolt监视器上调整扬声器音量...
- oracle10g 创建分区表,oracle10G分区的创建与维护Oracle分区表和本地索引
- c语言使用指针改数组逆置,用指针作函数参数,编写函数,将一个整型数组中的元素全部逆置。...
- python 向量元素判断_python;计算向量的元素
- plsql登录时显示无服务器,plsql登录提示ORA-12514:TNS:listener does not currently know of service…………...
- Docker安装mysql并连接