六:Day05_Spring Security01

一、Spring Security引入

Spring Security 是一个功能强大且高度可定制的身份验证和访问控制(认证和授权)框架。它是保护基于 Spring 应用程序的事实标准。

2. 认证授权

  • 认证授权实现平台所有用户的身份认证与用户授权功能。

2.1 什么是用户认证
  • 认证:Authentication

验证某个用户能否访问该系统。一般要求用户提供用户名和密码,手机号验证码等,系统通过校验来完成认证过程。

2.2 什么是用户授权
  • 授权:Authorization

用户认证通过后去访问系统的资源,系统会判断用户是否拥有访问资源的权限,只允许访问有权限的系统资源,没有权限的资源将无法访问。

3. 常见认证方式

3.1 Http basic 认证

http basic auth 通过使用Restful风格开发,每次请求服务器都携带用户名和密码。这种方式使用越来越少,因为不安全。

3.2 Session 认证

会话认证就是为一次请求认证在服务端创建一个Session对象,同时在客户端的浏览器段创建了一个Cookie对象(保存sessionID),通过客户端带上来Cookie对象来与服务器端Session对象匹配实现用户状态管理的。

3.3 Token 认证

使用基于Token的身份验证方法,在服务端不需要存储用户的登录记录。大概流程如下:

  1. 客户端使用用户名和密码请求登录

  2. 服务端收到请求,去验证用户名与密码

  3. 验证成功后,服务器颁发一个Token,再把这个Token发送给客户端

  4. 客户端收到Token以后把它存储起来

  5. 客户端每次向服务器端请求资源的时候需要带着Token

  6. 服务端收到请求,然后去验证客户端请求里面带着的Token,如果验证成功,就向客户端返回请求的数据。

3.4 OAuth2 认证

 例如:微信扫码认证,这是一种第三方认证的方式,这种认证方式是基于OAuth2协议实现。

OAUTH协议为用户资源的授权提供了一个安全的、开放而又简易的标准。同时,任何第三方都可以使用OAUTH认证服务,任何服务提供商都可以实现自身的OAUTH认证服务。

二、Spring Security介绍

  • 两个主要区域是“认证(authentication)”和“授权(authorization)”。这两点也是Spring Security重要核心功能。
  • Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架,它是一个专注于为 Java 应用程序提供身份验证和授权的框架。

Spring Security项目主页:Spring Security

Spring cloud Security: Spring Cloud Security

三、认证快速入门

1. 引入依赖

<!-- 引入web项目启动器 --> 
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入Spring Security启动器 --> 
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2. 创建控制器

@RestController
public class UserController {

    @RequestMapping("show")
    public String show(){
        return "Hello World!";
    }
}

3. 访问资源

导入spring-boot-starter-security启动器后,Spring Security已经生效,默认拦截全部请求,如果用户没有登录,跳转到内置登录页面。

4. 完成登录

5. 自定义登录用户名和密码

spring:
  security:
    user:
      name: root
      password: root

四、自定义认证逻辑(重要)

1. UserDetailsService接口

当什么也没有配置的时候,账号和密码是由Spring Security定义生成的,存储在内存中。

而在实际项目中账号和密码都是从数据库中查询出来的。 所以我们需要通过自定义逻辑控制认证(登录)逻辑。

如果需要自定义逻辑时,只需要实现UserDetailsService接口即可。

接口定义如下:

  • 默认策略:浏览器发起请求,经过拦截器:

    • 没有认证(没有登录),执行UserDetailsService接口实现类InMemoryUserDetailsManager中loadUserByUsername()方法,将输入的用户名和内存中用户名完成比较,用户名比较通过后,再比较输入的密码和内存中的密码。

  • 如果用户名和密码需要从数据库查询后再完成比较,重写UserDetailsService接口中的loadUserByUsername()方法即可。

    • 思路分析:

      没有认证(没有登录),执行UserDetailsService接口自定义实现类中loadUserByUsername()方法,根据用户名在数据库中查询数据,查询到了数据,再完成密码的比较。

1.2 返回值UserDetails
1.2.1 UserDetails接口

1.2.2 User实现类
  • User的全限定路径:

org.springframework.security.core.userdetails.User 此处经常和系统中自己开发的User类弄混。

  • 构造方法:

构造方法有两个。三个参数的构造方法实际上也是调用7个参数的构造方法。

3.自定义认证准备

2.1 准备流程
  1.  创建数据库表。
  2. 引入mybatis-plus,mysql,Spring Security相关依赖。
  3. 在配置文件中配置数据源和mybatis-plus相关配置。
  4. 创建实体类。
  5. UserServiceImp实现UserDetailsService接口,并重写loadUserByUsername方法。
  6. 创建配置类。将BCryptPasswordEncoder(实现PasswordEncoder接口 )交由Spring容器管理。
2.2 UserServiceImpl的编写
@Service
public class UserServiceImpl implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;

    /**
     * @param username  前端登录时提交的用户名
     * @return          用户的认证信息,要求必须为UserDetails接口的实现类
     * @throws UsernameNotFoundException  用户名没有找到时的异常
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, username);
        User myUser = userMapper.selectOne(wrapper);
        if (myUser == null){
            throw new UsernameNotFoundException("用户名不存在");
        }

        /*
         * org.springframework.security.core.userdetails.User为UserDetails接口的实现类。
         * 也可以自定义的User实现UserDetail接口提供对应的属性。
         * */
        //参数一:用户名  参数二:密码  参数三:权限(暂时不授权,创建个没有元素集合。不能指定null)
        ArrayList<GrantedAuthority> list = new ArrayList<>();
        org.springframework.security.core.userdetails.User user = new org.springframework.security.core.userdetails.User(
                myUser.getUsername(), myUser.getPassword(), list);
        return user;
    }
}
2.3 PasswordEncoder接口实现类
@Configuration
public class SecurityConfig {
    //密码比较,加密器
    @Bean
    public PasswordEncoder getPwdEncoder() {
        return new BCryptPasswordEncoder();
    }
}

2.PasswordEncoder接口

Spring Security要求容器中必须有PasswordEncoder接口实现类对象。进行密码校验和密码加密。

2.1 介绍

 encode()

将密码进行加密。可以用在注册用户中,将用户提交的密码加密后再存储到数据库中。

matches()

用户登录时,将新提交的密码进行加密,加密后和数据库中存储的已经加密的密码进行校验。密码匹配,返回true;不匹配,返回false。

第1个参数:新提交的密码。第2个参数:数据库中存储的已经加密的密码。

upgradeEncoding() 用于判断是否需要对密码进行再次加密,以使得密码更加安全, 默认:false不需要。

2.2 接口实现类

Spring security中有多种实现类完成密码加密:

  1. MD5算法的Md5PasswordEncoder(不再被广泛使用 )。
  2. SHA 算法的ShaPasswordEncoder。
  3. BCrypt算法的BCryptPasswordEncoder。BCryptPasswordEncoder是Spring Security官方推荐的密码解析器
2.3 实现类BCryptPasswordEncoder
@SpringBootTest
class SpringsecurityApplicationTests {

    @Test
    void contextLoads() {
        BCryptPasswordEncoder bpe = new BCryptPasswordEncoder();
        String encode = bpe.encode("123456");
        System.out.println(encode); 

        boolean matches = bpe.matches("123456", encode);
        System.out.println(matches); 
    }
}
2.4创建配置类
  • 创建配置类,将BCryptPasswordEncoder交由Spring容器管理。

@Configuration
public class SecurityConfig {
    //密码比较,加密器
    @Bean
    public PasswordEncoder getPwdEncoder() {
        return new BCryptPasswordEncoder();
    }
}

五、自定义登录页面(重点)

1. 创建登录页面

pring Security中不仅仅提供了登录页面,还支持用户自定义登录页面。

2. 修改配置类

  • 修改过滤器链中的配置

  1. 请求授权设置

    1. login.html 没有认证,可以被访问

    2. 其它请求没有认证,不可以被访问

  2. 自定义的登录的请求能够被拦截

    • 登录请求被拦截后,Spring Security会获取登录请求的用户名和密码,获取后调用自定义UserDetailsService接口实现类UserServiceImpl,从数据库中根据获取的用户名进行查询和后续的密码校验。

    • 注意:此过程不经过控制器。

    • 设置登录页面的访问路径

    • 设置登录请求的访问路径

    • 也可以设置登录成功后请求的资源

    • 也可以设置登录失败后请求的资源

  3. 自定义登录请求需要关闭CSRF。

@Configuration
public class SecurityConfig {
    //滤器链的相关设置
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        //请求授权设置。
        /*
         * antMatchers():对指定的请求url进行控制
         * permitAll():允许访问
         * anyRequest():任意一个请求url的控制
         * authenticated():认证后便可以访问
         * */
        http.authorizeRequests()
                .antMatchers("/login.html").permitAll()  //login.html不需要被认证
                .anyRequest().authenticated();  //其它任意的请求都必须被认证,必须认证(登录成功)后就可以直接访问

        //Form表单提交请求的处理。(可以为传统form表单提交,ajax提交)
        http.formLogin()
                .loginPage("/login.html")           //登录页面对应的地址,退出登录功能时会使用
                .loginProcessingUrl("/user/login")  //登录的url,SpringSecurity会拦截该请求,执行登录的处理
                .successForwardUrl("/user/ok")      //登录成功跳转的资源。默认只接收post请求
                .failureForwardUrl("/user/err");    //登录失败跳转的资源
                .usernameParameter("uname")        //自定义用户名参数名
                .passwordParameter("pwd");         //自定义密码参数名

        //关闭csrf防护
        http.csrf().disable();
        return http.build();
    }
    //密码比较,加密器
    @Bean
    public PasswordEncoder getPwdEncoder() {
        return new BCryptPasswordEncoder();
    }
}

六、认证流程源码分析

1. UsernamePasswordAuthenticationFilter

  • 用户提交登录请求后执行UsernamePasswordAuthenticationFilter

2 . AuthenticationManager

  • ProviderManager为AuthenticationManager接口的实现类实现了authenticate方法

3. DaoAuthenticationProvider

  • 调用父类AbstractUserDetailsAuthenticationProvider中的authenticate方法

  • 接收到UserDetails接口实现类User对象后,继续进行相关验证

  • 认证通过后会调用createSuccessAuthentication方法,在该方法中new 了一个 UsernamePasswordAuthenticationToken,因为到这里认证已经通过了,所以将 authorities(授权数据) 注入进去,并设置 authenticated 为 true,已经被认证。  

4. AbstractAuthenticationProcessingFilter

5. 流程总结

  1. 用户在浏览器中随意输入一个URL。

  2. Spring Security 会判断当前是否已经被认证(登录)如果已经认证,正常访问URL。如果没有被认证跳转到loginPage()对应的URL中,显示登录页面。

  3. 用户输入用户名和密码点击登录按钮后,发起请求。

  4. 如果url和loginProcessingUrl()一样才执行登录流程。否则需要重新认证。

  5. 执行登录流程时首先被UsernamePasswordAuthenticationFilter进行过滤,取出用户名和密码,放入到容器(UsernamePasswordAuthenticationToken)中。根据usernameParameter和passwordParameter获取用户名和密码,如果没有配置这两个方法,默认为请求参数名username和password,把UsernamePasswordAuthenticationToken 交给 AuthenticationManager对象进行匹配管理,在当前方法中会进行认证方式匹配(AuthenticationProvider )

  6. 我们使用表单是AbstractUserDetailsAuthenticationProvider,再次调用retrieveUser()执行自定义登录逻辑UserDetailsService的实现类。返回数据库中保存当前用户信息,然后调用三次检查方法

    1. preAuthenticationChecks.check(user);(账号是否锁定,账号是否启用,账号是否过期)

    2. additionalAuthenticationChecks(user,authentication);(密码是否为null,密码是否正确)

    3. postAuthenticationChecks.check(user);(密码是否过期)

  7. 经过AbstractAuthenticationProcessingFilter中doFliter()进行跳转:

    1. 登录成功,执行onAuthenticationSuccess方法,重定向到指定的资源。

      • 通过session存储用户的认证信息。一个客户端会分配一个线程,在seesion中存储时为了保证线程安全,使用ThreadLocal存储认证信息。

        • ThreadLocal:

        • 存储在ThreadLocal中的数据可以保证线程安全。

    2. 登录失败,执行onAuthenticationFailure方法,请求转发到指定的资源。

    • 注意:如果没有配置成功和失败转发URL,会跳转到用户访问的URL。

七、退出登录

1. 创建退出登录

  • 退出登录的请求路径为logout,会被过滤器拦截,拦截后会跳转到登录页面。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <a href="/logout">退出登录</a>
</body>
</html>

2. 设置退出登录

  • 注意:退出登录后会清除认证信息,只有配置了放行的资源才能够跳转

//退出登录相关自定义(可以不用定义)
http.logout()
        .logoutUrl("/mylogout")         //自定义退出访问的路径
        .logoutSuccessUrl("/user/login.html");  //自定义退出成功后跳转的资源       

3. logout流程源码分析

 

 

八、Remember Me

  • Spring Security 中Remember Me为 "记住我" 功能,Spring Security会自动把用户信息存储到数据源中,以后就可以不登录进行访问。

  • 用户只需要在登录表单中添加复选框,并且name="remember-me" value="true"。

1. 修改登录页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="user/login" method="post">
    用户名:<input type="text" name="uname"> <br>
    密码:<input type="text" name="pwd"> <br>
    记住我:<input type="checkbox" name="remember-me" value="true"> <br>
    <input type="submit">
</form>
</body>
</html>

2. 添加配置类

@Configuration
public class RememberMeConfig {
    @Autowired
    private DataSource dataSource;
    @Bean
    public PersistentTokenRepository getPersistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepositoryImpl=new JdbcTokenRepositoryImpl();
        jdbcTokenRepositoryImpl.setDataSource(dataSource);
        // 自动建表,第一次启动时需要,第二次启动时注释掉
        //jdbcTokenRepositoryImpl.setCreateTableOnStartup(true);
        return jdbcTokenRepositoryImpl;
    }
}

3. 修改SecurityConfig配置类

@Configuration
public class SecurityConfig {
    
    //注入UserDetailsService
    @Autowired
    private UserDetailsService userDetailsService;

    //滤器链的相关设置
    @Bean                                                     //新添加,令牌库
    public SecurityFilterChain filterChain(HttpSecurity http, PersistentTokenRepository tokenRepository) throws Exception {
		
        // 其它配置需要添加 ...

        //remember Me
        http.rememberMe()
                // .tokenValiditySeconds(120)     //单位:秒。默认2周
                // .rememberMeCookieDomain("/")   //设置cookie的域
                .tokenRepository(tokenRepository)
                .userDetailsService(userDetailsService);
        
        return http.build();
    }
    //密码比较,加密器
    @Bean
    public PasswordEncoder getPwdEncoder() {
        return new BCryptPasswordEncoder();
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HarmonyOS、ArkTS 备忘录(持续更新中)

Component 、Builder Component封装大的组件Builder自定义构建函数&#xff0c;可以理解为 构建页面的函数&#xff1b;Builder插槽多点&#xff0c;封装一些小的模块 组件状态管理 像素单位 export default 和 export之间的区别

Github 2023-12-14 开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2023-12-14统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量非开发语言项目5TypeScript项目2JavaScript项目1Jupyter Notebook项目1PHP项目1 基于项目的学习 创建周期&a…

企业计算机服务器中了halo勒索病毒怎么解密,勒索病毒解密数据恢复

在网络技术飞速发展的今天&#xff0c;越来越多的企业开始意识到企业数据安全的重要性&#xff0c;很多企业都会依赖数字化办公系统软件&#xff0c;并且通过系统软件将企业的重要数据存储在数据库中&#xff0c;为企业的生产运营提供了极大便利&#xff0c;但网络威胁一直存在…

探索云测试的方法:优化软件质量的新趋势

随着云计算技术的不断发展&#xff0c;云测试成为提高软件质量和效率的关键方法之一。本文将介绍一些云测试的方法&#xff0c;以帮助团队更好地应对不同的测试需求和挑战。 1. 云测试环境搭建 传统测试中&#xff0c;搭建测试环境可能需要大量的时间和资源。云测试通过提供可扩…

消息中间件比较

那都有哪些中间件可供选择呢。其实现在主流的消息中间件就4种&#xff1a;kafka、ActiveMQ、RocketMQ、RabbitMQ 下面我们来看一下&#xff0c;他们之间有什么区别&#xff0c;他们分别应该用于什么场景 ActiveMQ 我们先看ActiveMQ。其实一般早些的项目需要引入消息中间件&…

VS Code串口监视插件Serial Monitor

文章目录 初步使用参数设置VS Code插件 初步使用 Serial Monitor&#xff0c;即串行监视器&#xff0c;提供串口和TCP协议的通信监控功能。在插件栏搜索安装之后&#xff0c;按下Ctrl打开终端&#xff0c;终端界面会多出一个串行监视器选项卡&#xff0c;进入之后&#xff0c;…

linux中的od命令与hexdump命令

初步解读两个命令 在Linux中&#xff0c;"od"和"hexdump"命令都用于以十六进制和其他格式显示文件的内容。它们提供了对文件进行二进制查看和分析的功能。以下是它们的简要说明&#xff1a; od命令&#xff1a; “od”&#xff08;octal dump&#xff09;…

嵌入式开发板qt gdb调试

1&#xff09; 启动 gdbserver ssh 或者 telnet 登陆扬创平板 192.168.0.253&#xff0c; 进入命令行执行如下&#xff1a; chmod 777 /home/HelloWorld &#xff08;2&#xff09; 打 开 QTcreator->Debug->StartDebugging->Attach to Running Debug Server 进行…

web前端项目-影视网站开发

影视网站 本项目主要使用到了 HTML&#xff1b;CSS&#xff1b;JavaScript脚本技术&#xff1b;AJAX无刷新技术&#xff1b;jQuery等技术实现了动态影视网页 运行效果&#xff1a; 一&#xff1a;index.html <!DOCTYPE> <html lang"en"> <head>…

【图像分类】【深度学习】【Pytorch版本】 ResNeXt模型算法详解

【图像分类】【深度学习】【Pytorch版本】 ResNeXt模型算法详解 文章目录 【图像分类】【深度学习】【Pytorch版本】 ResNeXt模型算法详解前言ResNeXt讲解分组卷积(Group Converlution)分割-变换-合并策略(split-transform-merge)ResNeXt模型结构 ResNeXt Pytorch代码完整代码总…

OpenHarmony应用开发——在标准OpenHarmony上运行应用-标准OpenHarmony工程设置

一、前言 前面我们创建了一个工程并使其在HarmonyOS系统上运行&#xff0c;本文我们来阐述一下如何在标准的OpenHarmony开发板或系统上运行。 二、详细步骤 1.下载并配置OpenHarmony SDK 首先&#xff0c;打开Settings. 将SDK选择为OpenHarmony&#xff0c;第一次选择路径应该…

2-2基础算法-递归/进制转换

文章目录 一.递归二.进制转换 一.递归 1.数的计算 评测系统 #include <iostream> int countCombinations(int n) { //计算当然组合种数if (n 1) {return 1;}int count 1;//数字本身就是一个有效组合for (int i 1; i < n / 2; i) {count countCombinations(i);/…

风速预测(三)EMD-LSTM-Attention模型

目录 1 风速数据EMD分解与可视化 1.1 导入数据 1.2 EMD分解 2 数据集制作与预处理 2.1 先划分数据集&#xff0c;按照8&#xff1a;2划分训练集和测试集 2.2 设置滑动窗口大小为7&#xff0c;制作数据集 3 基于Pytorch的EMD-LSTM-Attention模型预测 3.1 数据加载&#…

Android Studio 实现音乐播放器

目录 一、引言 视频效果展示&#xff1a; 1.启动页效果 2.登录页效果 3.注册页效果 4.歌曲列表页效果 5.播放页效果 二、详细设计 1.登陆注册功能 2.音乐列表页面 2.音乐播放功能 三、源码获取 一、引言 Android初学者开发第一个完整的实例项目应该就属《音乐播放器…

redis:一、面试题常见分类+缓存穿透的定义、解决方案、布隆过滤器的原理和误判现象、面试回答模板

redis面试题常见分类 缓存穿透 定义 缓存穿透是一种现象&#xff0c;引发这种现象的原因大概率是遭到了恶意攻击。具体就是查询一个一定不存在的数据&#xff0c;mysql查询不到数据也不会直接写入缓存&#xff0c;就会导致这个数据的每次请求都需要查DB&#xff0c;数据库压力…

简洁应用框架VSEF的架构

本文介绍了一些简洁架构VSEF的一些框架结构理解&#xff0c;并且抛出了一些演化的主题&#xff0c;这些主题的不同思考会让系统发展成不同的风格&#xff0c;实际也是应用定位的必然结果。 总体 VSEF 架构理念 基本结构 演化 基本结构 入口 内核 依赖内核 简单逻辑 复杂…

「Leetcode」滑动窗口—长度最小的子数组

&#x1f4bb;文章目录 &#x1f4c4;题目✏️题目解析 & 思路&#x1f4d3;总结 &#x1f4c4;题目 209. 长度最小的子数组 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl1, …,…

小航助学2023年9月电子学会Scratch四级真题(含题库答题软件账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09; 单选题3.00分 删除编辑附件图文 答案:A 第1题角色为一个紫色圆圈&#xff0c;运行程序后&#xff0c;舞台上的图案是&#xff1f;&#xff08; &#xff09; A…

Android 动画 Lottie 如何使用

Android 动画 Lottie 如何使用 一、简介 Lottie 是Airbnb开源的一个面向 iOS、Android、React Native 的动画库&#xff0c;能分析 Adobe After Effects 导出的动画&#xff0c;并且能让原生 App 像使用静态素材一样使用这些动画&#xff0c;完美实现动画效果。 二、Lottie动…

RRC下的NAS层

无线资源控制&#xff08;Radio Resource Control&#xff0c;RRC&#xff09;&#xff0c;又称为无线资源管理&#xff08;RRM&#xff09;或者无线资源分配&#xff08;RRA&#xff09;&#xff0c;是指通过一定的策略和手段进行无线资源管理、控制和调度&#xff0c;在满足服…