Spring Security 02 搭建环境

news2024/9/26 1:23:15

搭建环境

导入依赖

<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>

编写 Controller

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello() throws JsonProcessingException {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Object principal = authentication.getPrincipal();
        return new ObjectMapper().writeValueAsString(principal);
    }

}

启动项目

访问:

http://localhost:8080/hello,页面会跳转到

http://localhost:8080/login 进行登入

- 默认⽤户名为: user
- 默认密码为: 控制台打印的 uuid 

 

这就是 Spring Security 的强⼤之处,只需要引⼊⼀个依赖,所有的接⼝就会⾃动保护起来!思考 ?

  • 为什么引⼊ Spring Security 之后没有任何配置所有请求就要认证呢?
  • 在项⽬中明明没有登录界⾯, 登录界⾯ 怎么来的呢?
  • 为什么使⽤ user 和 控制台密码 能登陆,登录时验证数据源存在哪⾥呢?

实现原理

Architecture :: Spring Security

Spring Security的Servlet支持是基于Servlet过滤器的客户端向应用程序发送一个请求,容器创建一个FilterChain,其中包含Filter实例和Servlet,应该根据请求URI的路径来处理HttpServletRequest。在Spring MVC应用程序中,Servlet 是 DispatcherServlet 的一个实例。

由于一个Filter只影响下游的Filter实例和Servlet,所以每个Filter的调用顺序是非常重要的。

Spring 提供了一个名为 Delegating FilterProxy 的过滤器实现,允许在Servlet容器的生命周期和 Spring 的ApplicationContext 之间建立桥梁。Servlet容器允许通过使用自己的标准来注册 Filter 实例,但它不知道 Spring 定义的 Bean。你可以通过标准的 Servlet 容器机制来注册 DelegatingFilterProxy,但将所有工作委托给实现 Filter的 Spring Bean。

DelegatingFilterProxy 的另一个好处是,它允许延迟查找 Filter Bean实例。这一点很重要,因为在容器启动之前,容器需要注册Filter实例。然而,Spring 通常使用 ContextLoaderListener 来加载 Spring Bean,这在需要注册Filter 实例之后才会完成。

 

Servlet 容器允许使用自己的标准来注册Filter实例,自定义过滤器并不是直接放在 Web 项⽬的原⽣过滤器链中,⽽是通过⼀个 FlterChainProxy 来统⼀管理。 Spring Security 中的过滤器链通过 FilterChainProxy 嵌⼊到 Web 项⽬的原⽣过滤器链中。 FilterChainProxy 作为⼀个顶层的管理者,将统⼀管理 Security Filter。

FilterChainProxy 本身是通过 Spring 框架提供的 DelegatingFilterProxy 整合到原⽣的过滤器链中。

 

servlet 与 spring 之间的联系:

https://www.cnblogs.com/shawshawwan/p/9002126.html

为什么不直接注册到 Servlet 容器 或者 DelegatingFilterProxy ?

SecurityFilterChain 中注册的是 Bean,这些 Bean 是注册在 FilterChainProxy 中的,相对于直接注册到 Servelt 容器 或者 DelegatingFilterProxy,FilterChainProxy提供了许多优势:

  • 它为 Spring Security 的所有 Servlet 支持提供了一个起点,方便代码调试
  • 由于 FilterChainProxy 是 Spring Security 使用的核心,它可以执行一些不被视为可有可无的任务
  • 它在确定何时应该调用 SecurityFilterChain 方面提供了更大的灵活性。在 Servlet 容器中,Filter 实例仅基于 URL 被调用。然而,FilterChainProxy 可以通过使用 RequestMatcher 接口,根据 HttpServletRequest 中的任何内容确定调用

源码解析

SpringBootWebSecurityConfiguration

这个类是 spring boot ⾃动配置类,通过这个源码得知,默认情况下对所有请求进⾏权限控制:


/**
 * {@link Configuration @Configuration} class securing servlet applications.
 *
 * @author Madhura Bhave
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
class SpringBootWebSecurityConfiguration {

    /**
     * The default configuration for web security. It relies on Spring Security's
     * content-negotiation strategy to determine what sort of authentication to use. If
     * the user specifies their own {@code WebSecurityConfigurerAdapter} or
     * {@link SecurityFilterChain} bean, this will back-off completely and the users
     * should specify all the bits that they want to configure as part of the custom
     * security configuration.
     */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnDefaultWebSecurity
    static class SecurityFilterChainConfiguration {

        @Bean
        @Order(SecurityProperties.BASIC_AUTH_ORDER)
        SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
            http.authorizeRequests().anyRequest().authenticated();
            http.formLogin();
            http.httpBasic();
            return http.build();
        }
    }

    /**
     * Adds the {@link EnableWebSecurity @EnableWebSecurity} annotation if Spring Security
     * is on the classpath. This will make sure that the annotation is present with
     * default security auto-configuration and also if the user adds custom security and
     * forgets to add the annotation. If {@link EnableWebSecurity @EnableWebSecurity} has
     * already been added or if a bean with name
     * {@value BeanIds#SPRING_SECURITY_FILTER_CHAIN} has been configured by the user, this
     * will back-off.
     */
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
    @ConditionalOnClass(EnableWebSecurity.class)
    @EnableWebSecurity
    static class WebSecurityEnablerConfiguration {

    }

}

这就是为什么在引⼊ Spring Security 中没有任何配置情况下,请求会被拦截的原因!

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(DefaultWebSecurityCondition.class)
public @interface ConditionalOnDefaultWebSecurity {

}
class DefaultWebSecurityCondition extends AllNestedConditions {

    DefaultWebSecurityCondition() {
        super(ConfigurationPhase.REGISTER_BEAN);
    }

    @ConditionalOnClass({ SecurityFilterChain.class, HttpSecurity.class })
    static class Classes {

    }

    @ConditionalOnMissingBean({
            org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.class,
            SecurityFilterChain.class })
    @SuppressWarnings("deprecation")
    static class Beans {

    }

}

通过上⾯对⾃动配置分析,我们也能看出默认⽣效条件为:

  • 条件⼀ classpath中存在 SecurityFilterChain.class, httpSecurity.class
  • 条件⼆ 没有⾃定义 WebSecurityConfigurerAdapter.class, SecurityFilterChain.class

补充说明:

@ConditionalOnClass:当项目中存在他条件中的某个类时才会使标有该注解的类或方法生效;

@ConditionalOnMissingBean:判断 Spring 容器中该 bean 实例是否存在,存在则不注入,没有就注入

流程分析 

 

  1. 请求 /hello 接⼝,在引⼊ spring security 之后会先经过⼀些列过滤器
  2. 在请求到达 FilterSecurityInterceptor时,发现请求并未认证。请求拦截下来,并抛出 AccessDeniedException 异常
  3. 抛出 AccessDeniedException 的异常会被 ExceptionTranslationFilter 捕获,这个 Filter 中会调⽤ LoginUrlAuthenticationEntryPoint#commence⽅法给客户端返回 302,要求客户端进⾏重定向到 /login ⻚⾯。
  4. 客户端发送 /login 请求。
  5. /login 请求会再次被拦截器中 DefaultLoginPageGeneratingFilter 拦截到,并在拦截器中返回⽣成登录⻚⾯。

默认用户生成

1.查看 SecurityFilterChainConfiguration.defaultSecurityFilterChain() ⽅法表单登录

2.处理登录为 FormLoginConfigurer 类中 调⽤ UsernamePasswordAuthenticationFilter 这个类实例

3.查看类中 UsernamePasswordAuthenticationFilter.attempAuthentication() ⽅法得知实际调⽤ AuthenticationManager 中 authenticate ⽅法

 4.调⽤ ProviderManager 类中⽅法 authenticate

5.调⽤了 ProviderManager 实现类中 AbstractUserDetailsAuthenticationProvider 类中⽅法

6.最终调⽤实现类 DaoAuthenticationProvider 类中⽅法⽐较 

 

看到这⾥就知道默认实现是基于 InMemoryUserDetailsManager 这个类,也就是内存的实现!

UserDetailService 

UserDetailService 是顶层⽗接⼝,接⼝中 loadUserByUserName ⽅法是⽤来在认证时进⾏⽤户名认证⽅法,默认实现使⽤是内存实现,如果想要修改数据库实现我们只需要⾃定义 UserDetailService 实现,最终返回 UserDetails 实例即可。

public interface UserDetailsService {

    /**
     * Locates the user based on the username. In the actual implementation, the search
     * may possibly be case sensitive, or case insensitive depending on how the
     * implementation instance is configured. In this case, the <code>UserDetails</code>
     * object that comes back may have a username that is of a different case than what
     * was actually requested..
     * @param username the username identifying the user whose data is required.
     * @return a fully populated user record (never <code>null</code>)
     * @throws UsernameNotFoundException if the user could not be found or the user has no
     * GrantedAuthority
     */
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

UserDetailServiceAutoConfigutation 

@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);

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

    private String getOrDeducePassword(SecurityProperties.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()));
        }
        if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) {
            return password;
        }
        return NOOP_PASSWORD_PREFIX + password;
    }

}

结论

  • 从⾃动配置源码中得知当 classpath 下存在 AuthenticationManager 类
  • 当前项⽬中,系统没有提供 AuthenticationManager.class、AuthenticationProvider.class、 UserDetailsService.class、AuthenticationManagerResolver.class实例

默认情况下都会满⾜,此时Spring Security会提供⼀个 InMemoryUserDetailManager 实例

@ConfigurationProperties(prefix = "spring.security")
public class SecurityProperties {
 
    private final User user = new User();

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

        /**
         * Default user name.
         */
        private String name = "user";

        /**
         * Password for the default user name.
         */
        private String password = UUID.randomUUID().toString();
        
        /**
         * Granted roles for the default user name.
         */
        private List<String> roles = new ArrayList<>();
        
        // ...
    }
}

这就是默认⽣成 user 以及 uuid 密码过程! 另外看明⽩源码之后,就知道只要在配置⽂

件中加⼊如下配置可以对内存中⽤户和密码进⾏覆盖。

spring.security.user.name=root
spring.security.user.password=root
spring.security.user.roles=admin,users

总体流程

 

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

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

相关文章

第31天-贪心-第八章 ● 122.买卖股票的最佳时机II ● 55. 跳跃游戏 ● 45.跳跃游戏II

文章目录1. 买卖股票的最佳时机2. 跳跃游戏3. 跳跃游戏 ||1. 买卖股票的最佳时机 - LeetCode链接 给你一个整数数组 prices &#xff0c;其中 prices[i] 表示某支股票第 i 天的价格。 在每一天&#xff0c;你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 …

docker安装jenkins安装流程(一)

1.下载安装jdk 官网&#xff1a;https://jdk.java.net/ https://www.oracle.com/java/technologies/downloads/ 上传到linux服务器 cd /usr mkdir jdk tar -zxvf 所要解压的文件名 编辑profile文件&#xff1a;vim /etc/profile 把光标移到最后一行&#xff0c;键盘按 i&…

技术分享 | 如何优雅的删除 Zabbix 的 history 相关历史大表

作者&#xff1a;徐文梁 爱可生DBA成员&#xff0c;一个执着于技术的数据库工程师&#xff0c;主要负责数据库日常运维工作。擅长MySQL,redis&#xff0c;其他常见数据库也有涉猎&#xff0c;喜欢垂钓&#xff0c;看书&#xff0c;看风景&#xff0c;结交新朋友。 本文来源&…

Vue动图数据表格,根据字段是否为空,控制表格列的隐藏和显示

所在前面的话&#xff0c;我是个前端小白&#xff0c;大佬请绕行&#xff0c;可能大佬觉得很简单&#xff0c;但是我真的花了好几个小时去解决&#xff0c;所以记录一下&#xff0c;下次也可以作为参考。 我主要是以第二种方式进行修改的 开门见山 简述问题&#xff1a;大家…

《这就是软件工程师》- 每位软件工程师值的看的一本书,尤其是刚刚步入IT行业的年轻人

文章目录第一部分&#xff5c;行业地图1、现实&#xff1a;为什么会有996&#xff1f;1&#xff09;行业处于特定的发展阶段2&#xff09;公司组织管理问题2、进阶&#xff1a;软件工程师的四大台阶1&#xff09;新手阶段【执行力】2&#xff09;进阶阶段【设计能力】3&#xf…

电子行业数字化工厂的发展现状如何

随着科技的不断发展&#xff0c;电子行业的生产制造方式也在不断地进步。数字化工厂建设是电子行业中的一个重要发展方向&#xff0c;它可以提高生产效率&#xff0c;降低生产成本&#xff0c;提高产品质量&#xff0c;减少人力资源的使用。本文将从数字化工厂的概念、发展历程…

Zookeeper总结

目录 1、概念 2、什么是Zookeeper 3、如何下载 4、部署运行 4.1、解压文件 4.2、修改配置文件 4.3、创建持久化目录 4.4、配置jdk 4.5、启动zookeeper服务 4.6、查看zookeeper运行状态 5、系统模型 5.1、数据模型&#xff08;文件系统&#xff09; 5.2、znode节点类型 持久…

Google FLASH-QUAD Transformer模型的设计雷点

这个模型用来做无序弱监督分类&#xff0c;效果好&#xff0c;特别是收敛速度比标准多头Attention层快多了&#xff0c;完全没得比。 问题1 但这模型我用来做自回归生成&#xff0c;非常垃圾。 同时尝试了 GPT 和 T5 这两种模型结构的设计&#xff0c;明明Loss正常下降&#…

环境变量相关知识

目录 目录 谢谢你的阅读&#xff0c;这是对我最大的鼓舞 先说结论&#xff1a; 开始论述&#xff1a; 让我们举个例子 相关指令 创建本地变量 创建环境变量 方法一&#xff1a; 方法二&#xff1a; 删除环境变量 子进程中也有环境变量 第一种&#xff1a; 第二种 …

【PlumGPT】与PlumGPT开启智能对话之旅

文章目录 一、前言二、PlumGPT介绍篇三、PlumGPT登录篇四、PlumGPT体验篇1、与PlumGPT聊天2、让PlumGPT翻译3、让PlumGPT创作4、请PlumGPT写推荐信5、让PlumGPT展示图片6、让PlumGPT充当百科小助手 五、PlumGPT总结篇 PlumGPT入口体验链接&#xff1a;https://plumgpt.com 一、…

【Kubernetes】 故障转移和自愈能力机制详解

文章目录一. 引言1. 介绍Kubernetes2. 故障转移和自愈能力的重要性二. Kubernetes 概览1. Kubernetes 架构2. Kubernetes 组件和功能三. 故障转移1. 如何定义故障转移2. Kubernetes 中的故障转移机制2.1 健康检查2.2 Pod 和 ReplicaSet2.3 控制器和故障转移3. Pods和ReplicaSet…

LS1028+TSN+Vxworks实时系统解决方案

TSN&#xff08;时间敏感网络&#xff09;正在各类市场兴起。它可以为过去不曾存在的全新应用提供实时通信。 例如&#xff0c;将控制功能从驱动中解耦出来并移至边缘计算。下一代自动控制器可以并行处理视频流和控制流等各种工作负载&#xff0c;在一台机器上运行全部深度学习…

【高危】Google Chrome V8 类型混淆漏洞(CVE-2023-2033)

漏洞描述 Google Chrome V8是Google开源的JavaScript和WebAssembly引擎&#xff0c;被用在Chrome和Node.js等浏览器和平台中。 该项目受影响版本存在类型混淆漏洞&#xff0c;攻击者可通过诱导用户打开恶意链接来触发此漏洞&#xff0c;可能导致浏览器崩溃或执行任意代码。由…

跟姥爷深度学习2 TensorFlow的基本用法

一、前言 前面我们浅用TensorFlow做了个天气预测&#xff0c;虽然效果不咋样&#xff0c;但算是将整个流程跑通了。这一篇我们在之前基础上对TensorFlow的一些参数进行简单介绍&#xff0c;在接口文件的基础上了解各参数的简单含义和用法。 二、再次构建模型 我们先将之前的…

基于组合双向拍卖的共享储能机制研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

规模化敏捷框架之LeSS

Scrum 是目前比较流行的敏捷框架&#xff0c;Scrum 敏捷开发团队由产品负责人&#xff08;Product Owner&#xff09;、敏捷教练&#xff08;Scrum Master&#xff09;、软件开发人员和其他合作人员组成。团队在这个指导框架下协同工作、开发、创造和交付有价值的产品来解决复杂…

易基因:METTL3介导的m6A甲基化谱调控肌肉干细胞成肌细胞状态转换|发育分化

2020年9月29日&#xff0c;《Cell Death Discovery》&#xff08;IF: 7.109&#xff09;杂志发表了题为“A defined N6-methyladenosine (m6A) profile conferred by METTL3 regulates muscle stem cell/myoblast state transitions”的研究论文&#xff0c;研究通过MeRIP-seq&…

静态时序分析Static Timing Analysis2——建立时间和保持时间的时序检查

文章目录前言一、建立时间检查1、寄存器到寄存器2、输入端口到寄存器3、寄存器到输出端口4、输入端口到输出端口二、保持时间检查1、寄存器到寄存器2、输入端口到寄存器3、寄存器到输出端口4、输入端口到输出端口前言 2023.4.11 继续学习STA&#xff0c;前面听的感觉好迷糊&am…

Android---性能优化之图片压缩

质量压缩 质量压缩会用到 Bitmap.compress()。 public boolean compress(Bitmap.CompressFormat format, int quality, OutputStream stream); 这个方法有三个参数&#xff1a; Bitmap.CompressFormat format&#xff1a;图像的压缩格式&#xff08;jpeg ,png, webp&#xf…

C++STL大全----容器篇(上)

&#xff08;一&#xff09;概念 STL&#xff0c;英文全称 standard template library&#xff0c;中文可译为标准模板库或者泛型库&#xff0c;其包含有大量的模板类和模板函数&#xff0c;是 C 提供的一个基础模板的集合&#xff0c;用于完成诸如输入/输出、数学计算等功能。…