Spring Security认证之基本认证

news2025/7/16 15:21:44

本文内容来自王松老师的《深入浅出Spring Security》,自己在学习的时候为了加深理解顺手抄录的,有时候还会写一些自己的想法。

快速入门

        在Spring Boot项目中使用Spring Security非常方便,创建一个新的Spring Boot项目我们只要引入Web和Spring Security依赖即可,具体的pom依赖如下:

         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        然后我们在项目中提供一个简单的测试/hello接口,代码如下:

/**
 * @author tlh
 * @date 2022/11/15 21:25
 */
@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "hello spring security";
    }
}

        接下来我们启动项目,/hello接口就会被Spring Seciryt保护起来。当用户访问/hello接口是就会跳转到登录页面(如下图),用户登录成功之后才能正常访问/hello接口。

        默认的登录用户名是user,登录密码则是一个随机生成的UUID字符串,在项目的启动日志中可以看到(也就意味着没项目启动密码都会发生变化,这里只是我们体验Spring Security使用的,后面我们会把用户名和密码存到数据库)

        输入默认的用户名和密码就能正常的访问/hello接口了。这里简单的体验下了Spring Security的强大之处,只需要映入一个简单的依赖所有的接口就会被自动保护起来。

流程分析

        通过几个简单的流程图来看一下上面案例中的请求流程:

  1.  客户端(浏览器)发起请求去访问/hello接口,这个接口默认是需要认证之后才能访问的。
  2. 这个请求户走一遍Spring Security中的过滤器链,在最后的FilterSecurityInterceptor过滤器中拦截下来。因为系统中发现用户没有被认证,请求拦截下来之后会抛出AccessDeniedException异常。
  3. 抛出的AccessDeniedException异常在ExceptionTranslationFilter过滤器中被捕获,ExceptionTranslationFilter过滤器通过调用LoginUrlAuthenticationEntryPoint的commence方法给客户端返回302,要求客户端重定向到/login页面。
  4. 客户端发送/login请求
  5. /login请求被DefaultLoginPageGeneratinFilter过滤器拦截下来,并在改过滤器中返回登录页面。所以用户访问/hello接口会先看到登录页面。

        整个过程中,相当于客户端发送了两次请求,第一个请求是/hello,服务端收到之后返回302,请求客户端重定向到/longin,于是客户端又发送了/login请求。

        此时去理解这个流程可能还有点困哪,等看完接下的文章之后再回头来看这个流程图应该就会比较清晰了。

原理分析

        虽然开发者只是引入了一个Spring Security相关的依赖,代码并不多,但是Spring Security背后为我们默默的做了很多事情:

  • 开启Spring Security自动化配置。开启后,Spring Security会自动创建一个名为springSecurityFilterChain的过滤器并注入到Spring容器中,这个过滤器将负责所有的安全管理,包括用户的认证、授权、重定向到登录页面等(springSecurityFilterChain实际上代理了Spring Security中的过滤器链)
  • 创建一个UserDetailsService实例,UserDetailsService负责提供用户数据,默认用户数据是基于内存的用户,用户名为user,密码则是随机生成的UUID字符串
  • 给用户生成一个默认的登录页面

默认用户生成

        Spring Security中定义了UserDetails接口来规范开发者自定义用户对象,这样方便一些旧系统、用户表已经固定的系统集成Spring Security认证体系中。

        UserDetails接口定义如下:

public interface UserDetails extends Serializable {
    
    //返回当前用户所具备的权限
    Collection<? extends GrantedAuthority> getAuthorities();

    //返回当前用户的密码
    String getPassword();

    //返回当前用户名
    String getUsername();

    //返回当前用户是否已经过期
    boolean isAccountNonExpired();
    //返回当前用户是否被锁定
    boolean isAccountNonLocked();

    //返回当前用户的凭证是否未过期
    boolean isCredentialsNonExpired();

    //返回当前账户是否可用
    boolean isEnabled();
}

        负责提供用户数据的接口是UserDetailsService。UserDetailsService接口中只有一个查询用户的方法,代码如下:

public interface UserDetailsService {
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

        loadUserByUsername只有一个username参数,这是用户在认证时传入的用户名,最常见的就是表单中输入的用户名。开发者在这里拿到用户名之后,再去数据库中查询用户,最终返回一个UserDetails实例。在实际开发中,一般开发者需要自定义UserDetailsService的实现。如果开发者没有自定义UserDetailsService的实现,Spring Securit也为UserDetailsService提供了默认实现:

  • UserDetailsManager:这个是一个扩展接口,新增了添加用户、跟新用户、删除用户、修改密码、判断用户是否存在等等方法。
  • JdbcDaoImpl:实现了通过spring-jdbc冲数据库中查询用户的方法
  • JdbcUserDetailsManager:继承自JdbcDaoImpl同时又实现了UserDetailsManager接口。这里有一定的局限性,因为操作数据的sql都是写好的不够灵活。因此,在实际开发中JdbcUserDetailsManager使用的并不多。
  • InMemoryUserDetailsManager:实现了UserDetailsManager中关于用户的增、删、改、查方法,不过都是基于内存操作,数据并没有持久化。

        当我们使用Spring Security时,如果仅仅引入一个Spring Security的依赖,则默认使用的InMemoryUserDetailsManager。伙伴们都知道,Spring Boot之所以能做到零配置使用Spring Security,就是因为它提供了众多的自动化配置类。其中UserDetailsService的自动化配置类就是UserDetailsAotuConfiguration。如下:

@AutoConfiguration
@ConditionalOnClass({AuthenticationManager.class})
@ConditionalOnBean({ObjectPostProcessor.class})
@ConditionalOnMissingBean(
    value = {AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class, AuthenticationManagerResolver.class},
    type = {"org.springframework.security.oauth2.jwt.JwtDecoder", "org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector", "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository", "org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository"}
)
public class UserDetailsServiceAutoConfiguration {
    private static final String NOOP_PASSWORD_PREFIX = "{noop}";
    private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern.compile("^\\{.+}.*$");
    private static final Log logger = LogFactory.getLog(UserDetailsServiceAutoConfiguration.class);

    public UserDetailsServiceAutoConfiguration() {
    }

    @Bean
    @Lazy
    public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, ObjectProvider<PasswordEncoder> passwordEncoder) {
        User user = properties.getUser();
        List<String> roles = user.getRoles();
        return new InMemoryUserDetailsManager(new UserDetails[]{org.springframework.security.core.userdetails.User.withUsername(user.getName()).password(this.getOrDeducePassword(user, (PasswordEncoder)passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)).build()});
    }

    private String getOrDeducePassword(User user, PasswordEncoder encoder) {
        String password = user.getPassword();
        if (user.isPasswordGenerated()) {
            logger.warn(String.format("%n%nUsing generated security password: %s%n%nThis generated password is for development use only. Your security configuration must be updated before running your application in production.%n", user.getPassword()));
        }

        return encoder == null && !PASSWORD_ALGORITHM_PATTERN.matcher(password).matches() ? "{noop}" + password : password;
    }
}

        从上面的代码可以看到,有两个比较重要的条件使系统自动提供一个InMemoryUserDetailsManager的实例:

  • 当前的classpath下没有AuthenticationManager类
  • 当前项目中,系统没有提供AuthenticationManager, AuthenticationProvider, UserDetailsService, ClientRegistrationRepository实例

        默认情况下,上面的条件都满足。此时Spring Security会提供一个InMemoryUserDetailsManager实例。从InMemoryUserDetailsManager方法中看到,用户信息来自于SecurityProperties的getUser方法。我们就能看到默认用户的名字为:user,密码则是UUID的随机字符串。

@ConfigurationProperties(
    prefix = "spring.security"
)
public class SecurityProperties {
    public static final int BASIC_AUTH_ORDER = 2147483642;
    public static final int IGNORED_ORDER = -2147483648;
    public static final int DEFAULT_FILTER_ORDER = -100;
    private final SecurityProperties.Filter filter = new SecurityProperties.Filter();
    private final SecurityProperties.User user = new SecurityProperties.User();

    public SecurityProperties() {
    }

    public SecurityProperties.User getUser() {
        return this.user;
    }

    public SecurityProperties.Filter getFilter() {
        return this.filter;
    }

    public static class User {
        private String name = "user";
        private String password = UUID.randomUUID().toString();
        private List<String> roles = new ArrayList();
       //省略get/set方法
    }

}

        我们看到SecurityProperties 类在加载的时候还可从配置文件中读取信息(@ConfigurationProperties注解起的作用),前缀为spring.security。这就意味着我们可以在配置文件中(application.properties)配置默认用户名和密码:

spring.security.user.name=tlh
spring.security.user.password=123456
spring.security.user.roles=admin,users

        配置完成之后,重启项目此时登录名就是tlh,登录密码就是123456,登录成功之后用户具备admin和users两个角色。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/8568.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java项目:前后端分离SpringBoot+Vue+Element的校内跑腿平台

收藏点赞不迷路 关注作者有好处 项目编号&#xff1a;BS-XX-155 一&#xff0c;项目简介 近年来&#xff0c;随着国内都市化的发展&#xff0c;生产生活节奏变快、各种往来频繁。经济的高速发展催生出“懒人经济”。不想走出家门&#xff0c;饭菜可以送上门;不方便交水、电、…

Java中restTemplate的使用

原文链接 代码地址 本文介绍restTemplate基础用法。 Java中get和post的用法请参考&#xff1a;https://mp.weixin.qq.com/s/mC0D1nuCqIori5bWtLorWQ 1 提供get/post接口 1.1 Controller RestController RequestMapping("/homepage") public class MyController…

电脑如何清理重复文件,查找电脑重复文件的软件

在电脑上面&#xff0c;不论是我们可以保存的&#xff0c;还是自动缓存的&#xff0c;都会有大量的重复文件&#xff0c;可能我们自己并没有发现&#xff0c;占据着电脑大量的空间&#xff0c;长此以往下去&#xff0c;会让电脑变得卡顿&#xff0c;我们就需要来清理一下这些不…

低代码维格云甘特视图入门教程

功能简介 低代码维格云甘特图主要通过条状图来显示某些时间相关的活动(任务、阶段、项目等)随着时间进展的情况,以便管理者直观地查看活动进度,把控全局。又称为时间视图、横道图、条状图(Bar chart)。 低代码维格云甘特图适用场景 项目管理生产管理其他领域:建筑、IT软件…

嵌入式FreeRTOS学习八,xTaskCreate创建任务的细节以及恢复中断任务实现

一.创建任务函数xTaskCreate 任务也不是很复杂的东西&#xff0c;任务也就是一个函数xTaskCreate。简单得说&#xff0c;创建一个任务&#xff0c;你得提供它的执行函数&#xff0c;你得提供它的栈的大小&#xff0c;函数的执行空间&#xff0c;函数的优先级等重要的条件。因为…

IPWorks EDI Translator Delphi Edition

IPWorks EDI Translator Delphi Edition 一套轻量级可编程EDI解析和翻译组件。 IPWorks EDI转换器包括便于电子数据交换(EDI)解析、翻译和验证的软件组件。这些组件包括灵活的模式支持&#xff0c;使开发人员能够使用各种模式格式&#xff0c;从而更容易与现有EDI处理应用程序集…

Maven项目属性与版本管理

本次将介绍两个内容&#xff0c;分别是&#xff1a; 属性版本管理 1. 属性 1.1 问题分析 我们先来分析一下问题&#xff1a; 前面在父工程中的dependencyManagement标签中对项目中所使用的jar包版本进行了统一的管理&#xff0c;但是如果在标签中有如下的内容&#xff1a;…

React源码分析4-深度理解diff算法

上一章中 react 的 render 阶段&#xff0c;其中 begin 时会调用 reconcileChildren 函数&#xff0c; reconcileChildren 中做的事情就是 react 知名的 diff 过程&#xff0c;本章会对 diff 算法进行讲解。 diff 算法介绍 react 的每次更新&#xff0c;都会将新的 ReactElem…

[安卓逆向]IDA Pro的认识及使用

[安卓逆向]IDA Pro的认识及使用 软件介绍 IDA Pro全称是交互式反汇编器专业版&#xff0c;人们其简称为IDA&#xff0c;IDA pro 是业界最成熟、先进的反汇编工具之一&#xff0c;是目前最棒的一个静态反编译软件&#xff0c;为众多0day世界的成员和ShellCode安全分析人士不可…

指纹浏览器是什么?可以用来解决流量套利的什么问题?

套利是一个永远不会过期的形式&#xff0c;由于信息差永远存在&#xff0c;有信息差就有套利空间。流量套利是购买和转售流量的过程。套利专家通常通过购买廉价流量并以更好的价格出售来赚取收入。他们把流量导流到广告商的网站上&#xff0c;满足广告商希望客户访问自己的网站…

理解Linux权限(一)

理解Linux文件权限 Permission Groups(权限组) 根据权限组划分&#xff1a;每个文件和目录都有3种使用者(用户) ower(所有者) - 所有者的权限仅适用于文件和目录的所有者&#xff0c;不会影响其他用户的操作&#xff1b;group(所属组) - 所属组的权限仅适用于已分配的文件和…

Transwarp Inceptor介绍

Transwarp Inceptor是星环科技推出的用于数据仓库和交互式分析的大数据平台软件&#xff0c;它基于Hadoop和Spark技术平台打造&#xff0c;加上自主开发的创新功能组件&#xff0c;有效的解决了企业级大数据数据处理和分析的各种技术难题&#xff0c;帮助企业快速的构建和推广数…

进化吧,MySQL锁!无锁->偏向锁->轻量级锁->重量级锁(请自动脑补数码宝贝进化音)

写在前边 走到哪都有各种琐事&#xff0c;在MySQL中咱已经聊透了各种琐事 ->MySQL锁机制&&事务&#xff0c;今天来看看Java里边的锁升级过程&#xff0c;以及各种锁之间的比较&#xff0c;悲观乐观&#xff0c;粗化消除~ 四种锁的Markword 优先程度 偏向锁->轻量…

【FPGA】FPGA实现IIC协议读写EEPROM(三) ----- 汇总篇

IIC协议读写EEPROM一、功能分析/模块划分二、状态转移图1、EEPROM读写控制状态转移图2、IIC接口驱动状态转移图三、工程代码实现1、顶层模块2、EEPROM读写控制模块3、IIC接口驱动模块4、参数配置5、其他模块四、仿真测试五、上板验证写在前面 FPGA实现IIC协议读写EEPROM相关文章…

【附源码】计算机毕业设计JAVA教学辅助系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven Vue 等等组成&#xff0c;B/…

【Qt】控件探幽——QWidget

注1&#xff1a;本系列文章使用的Qt版本为Qt 6.3.1 注2&#xff1a;本系列文章常规情况下不会直接贴出源码供复制&#xff0c;都以图片形式展示。所有代码&#xff0c;自己动手写一写&#xff0c;记忆更深刻。 本文目录探索QWidget1、ui文件最后会变成什么&#xff1f;2、如何改…

在 OpenHarmony 轻量设备开发应用

本文档旨在讲解新建 Helloworld 项目步骤、固件包烧录到 BES2600WM 开发板、实现 js 和 C 代码的通讯。该 Demo 重点体现的是 OpenAtom OpenHarmony&#xff08;以下简称“OpenHarmony”&#xff09; 3.1 Beta 系统轻量设备 js 和 C 的交互能力&#xff0c; 效果如图 &#xf…

dpdk PMD

PMD是Poll Mode Driver的缩写&#xff0c;即基于用户态的轮询机制的驱动 在不考虑vfio的情况下&#xff0c;PMD的结构图如下 虽然PMD是在用户态实现设备驱动&#xff0c;但还是依赖于内核提供的策略。其中uio模块&#xff0c;是内核提供的用户态驱动框架&#xff0c;而igb_uio…

深度探讨react-hooks实现原理

react hooks 实现 Hooks 解决了什么问题 在 React 的设计哲学中&#xff0c;简单的来说可以用下面这条公式来表示&#xff1a; UI f(data)等号的左边时 UI 代表的最终画出来的界面&#xff1b;等号的右边是一个函数&#xff0c;也就是我们写的 React 相关的代码&#xff1b…

最新最全面的Spring详解(一)——Spring概述与IOC容器

前言 本文为 【Spring】Spring概述与IOC容器 相关知识&#xff0c;下边将对Spring概述&#xff0c;IOC容器&#xff08;包括&#xff1a;IOC概述、配置元数据、容器实例化与使用、Bean的概述、依赖注入 Dependency Injection、Bean 作用范围&#xff08;作用域&#xff09;、更…