提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框架源码中的认证流程相关推荐

  1. 手把手带你撸一遍vue-loader源码

    前言 前面写过两篇webpack实战的文章: webpack实战之(手把手教你从0开始搭建一个vue项目) 手把手教你从0开始搭建一个vue项目(完结) 强烈建议小伙伴们去看一下前面几个章节的内容, ...

  2. 从框架源码中学习创建型设计模式

    文章目录 从框架源码中解读创建型设计模式 工厂模式 案例一:RocketMQ源码-创建Producer生产者 案例二:RocketMQ源码-创建过滤器工厂 抽象工厂 案例一:Dubbo源码-创建缓存的 ...

  3. 从框架源码中学习结构型设计模式

    文章目录 从框架源码学习结构型设计模式 适配器模式 应用实例 案例一:dubbo框架日志适配器 Logger接口 日志实现类 Logger适配器接口 LoggerAdapter实现类 Logger日志 ...

  4. 手把手带你在集成SpringSecurity的SpringBoot应用中添加短信验证码登录认证功能

    本文目录 前言 1 自定义AuthenticationToken类 2 自定义AuthenticationProvider类 3 自定义MobilePhoneAuthenticationFilter ...

  5. Spring Cloud微服务实战:手把手带你整合eurekazuulfeignhystrix(附源码)

    Spring Cloud微服务实战:手把手带你整合eureka&zuul&feign&hystrix(附源码) Spring Cloud简介 Spring Cloud是一个基于 ...

  6. 手把手带你快速实现直播平台源码聊天室

    工具要求: (1) Android Studio 3.2或更高版本. (2) SDK targetVersion至少为26. 本文直播平台源码聊天室是集成环信IM SDK实现聊天功能,及实现发送礼物. ...

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

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

  8. Nacos源码中为什么使用了String.intern方法?

    前言 面试的时候经常被问到String的intern方法的调用及内存结构发生的变化.但在实际生产中真正用到过了吗,看到过别人如何使用了吗? 最近阅读Nacos的源码,还真看到代码中使用String类的 ...

  9. php直播pk规则,直播源码中的主播PK功能是如何实现的

    直播行业为赢得更广泛用户的青睐,自然要不断开发更有趣的玩法.模式,在直播源码中加入主播PK功能就是一种提高直播互动性.激发用户好胜心的方法,一方面这种方法可以吸引更多用户观看,增加主播的曝光率,另一方 ...

最新文章

  1. 用户管理之用户的查询获取
  2. iOS 新特性分列式 之 iOS 8.x - 主要内容:应用扩展、手动对焦、Size Class
  3. Unity的Deferring the Resolution of Objects
  4. MPLS自身的优点所带来的网络便捷—Vecloud微云
  5. 【小白学习C++ 教程】十二、C++面向对象编程中的构造函数和析构函数
  6. c# winform 中的坐标系
  7. 胶囊路由_评论:胶囊之间的动态路由
  8. 数据结构——各排序算法的比较
  9. 使用 angular directive 和 json 数据的 D3 带标签 donut chart示例
  10. foobar2000实现用手机远程控制PC命令行版
  11. c语言的编译器还真是不好理解...
  12. Windows11 开发版bt磁力链种子及其他下载地址整理
  13. 精灵3P+Pix4D简单航测详细应用教程
  14. word2013、word2016、word2019标题序号变黑色竖线解决方法
  15. 新鲜出炉的三维动画应用领域,学3D建模好工作找不完
  16. 一维数组新老数据对比修改字段
  17. ZLIB 压缩的数据格式规范
  18. 写一个自动回复的聊天机器人
  19. switch日文键盘打中文_12月有哪些Switch游戏值得期待?
  20. Android应用程序签名 debug签名

热门文章

  1. JVM调优总结 -Xms -Xmx -Xmn -Xss(转载)
  2. 西南交大计算机专硕就业怎么样,国内四所交通大学,有985也有211,就业、深造容易,值得报考...
  3. java list 去空字符串_【JAVA基础】list和字符串判空
  4. python海龟图 如何控制线的粗细_使用Python中的高级turtle(海龟)作图方法
  5. mac hdmi 不能调整音量_如何使用Mac的媒体键在DisplayPortHDMI或Thunderbolt监视器上调整扬声器音量...
  6. oracle10g 创建分区表,oracle10G分区的创建与维护Oracle分区表和本地索引
  7. c语言使用指针改数组逆置,用指针作函数参数,编写函数,将一个整型数组中的元素全部逆置。...
  8. python 向量元素判断_python;计算向量的元素
  9. plsql登录时显示无服务器,plsql登录提示ORA-12514:TNS:listener does not currently know of service…………...
  10. Docker安装mysql并连接