Spring Security-02-Spring Security认证方式-HTTP基本认证、Form表单认证、HTTP摘要认证、前后端分离安全处理方案

news2024/11/27 18:42:35

Lison <dreamlison@163.com>, v1.0.0, 2024.06.01

Spring Security-02-Spring Security认证方式-HTTP基本认证、Form表单认证、HTTP摘要认证、前后端分离安全处理方案

文章目录

  • Spring Security-02-Spring Security认证方式-HTTP基本认证、Form表单认证、HTTP摘要认证、前后端分离安全处理方案
    • 认证方式
    • HTTP基本认证
      • 基本认证简介
      • 基本认证核心API
      • 基本认证的步骤
      • 基本认证的弊端
      • HTTP基本认证代码实现
        • 创建SecurityConfig配置类
        • 测试验证
      • Basic认证详解
        • 基本认证过程
        • 注销Basic认证
    • Form表单认证
      • 表单认证简介
      • 表单认证效果
      • 表单认证中的预置url和页面
      • 自定义表单认证配置
        • 创建SecurityConfig配置类
        • 测试应用
      • 自定义表单认证的登录界面
        • 服务端代码定义
        • 页面定义
        • 测试应用
      • 细化表单认证配置
        • 定义SecurityConfig类
        • 自定义登录页面
        • 定义错误处理页面
        • 测试验证
    • HTTP摘要认证
      • HTTP摘要认证简介
      • HTTP摘要认证核心参数
      • 摘要认证代码实现
        • 编写测试接口
        • 创建SecurityConfig配置类
        • 测试接口
        • HTTP摘要认证弊端
    • 前后端分离时的安全处理方案
      • 前后端分离简介
      • 认证处理时的相关API
        • 页面跳转的相关API
        • 返回JSON格式的处理器
      • 认证成功时的处理方案
        • 1. successHandler()方法
        • 2. onAuthenticationSuccess参数
        • 定义SecurityAuthenticationSuccessHandler类
        • 配置successHandler
        • 验证结果
      • 认证失败时的处理方案
        • failureHandler()
        • 代码实现
        • 配置failureHandler
        • 验证结果
      • 退出登录时的处理方案
        • 1. logoutSuccessHandler()
        • 2. 定义SecurityLogoutSuccessHandler类
        • 配置logoutSuccessHandler
        • 验证结果
      • 未认证时的处理方案
        • 1. authenticationEntryPoint()
        • 2. 定义SecurityAuthenticationEntryPoint类
        • 配置authenticationEntryPoint
        • 验证结果

认证方式

认证: 所谓的认证,就是用来判断系统中是否存在某用户,并判断该用户的身份是否合法的过程,解决的其实是用户登录的问题。认证的存在,是为了保护系统中的隐私数据与资源,只有合法的用户才可以访问系统中的资源。

在Spring Security中,常见的认证方式可以分为HTTP层面和表单层面,常见的认证方式如下:

  • HTTP基本认证;
  • Form表单认证;
  • HTTP摘要认证;

HTTP基本认证

基本认证简介

在Spring Security 4.x版本中,默认采用的登录方式是Http基本认证,该方式会弹出一个对话框,要求用户输入用户名和密码。在每次进行基本认证请求时,都会在Authorization请求头中利用Base64对 “用户:密码” 字符串进行编码。这种方式并不安全,并不适合在Web项目中使用,但它是一些现代主流认证的基础,而且在Spring Security的OAuth中,内部认证的默认方式就是用的Http基本认证。

基本认证核心API

在这里插入图片描述

执行流程如下:

Filter->构造Token->AuthenticationManager->转给Provider处理->认证处理成功后续操作或者不通过抛异常

基本认证的步骤

HTTP基本认证是在RFC2616标准中定义的一种认证模式,它以一种很简单的方式与用户进行交互。HTTP基本认证可以分为如下4个步骤:

  • ①. 客户端首先发起一个未携带认证信息的请求;
  • ②. 然后服务器端返回一个401 Unauthorized的响应信息,并在WWW-Authentication头部中说明认证形式:当进行HTTP基本认证时,WWW-Authentication会被设置为Basic realm=“被保护的页面”;
  • ③. 接下来客户端会收到这个401 Unauthorized响应信息,并弹出一个对话框,询问用户名和密码。当用户输入后,客户端会将用户名和密码使用冒号进行拼接并用Base64编码,然后将其放入到请求的Authorization头部并发送给服务器;
  • ④. 最后服务器端对客户端发来的信息进行解码得到用户名和密码,并对该信息进行校验判断是否正确,最终给客户端返回响应内容。

基本认证的弊端

HTTP基本认证是一种无状态的认证方式,与表单认证相比,HTTP基本认证是一种基于HTTP层面的认证方式,无法携带Session信息,也就无法实现Remember-Me功能。另外,用户名和密码在传递时仅做了一次简单的Base64编码,几乎等同于以明文传输,极易被进行密码窃听和重放攻击。所以在实际开发中,很少会使用这种认证方式来进行安全校验。

HTTP基本认证代码实现

创建SecurityConfig配置类

这里我们先创建一个config配置类,命名为SecurityConfig,并且继承自WebSecurityConfigurerAdapter父类,代码如下:

package com.lison.springsecurity.config.security;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * @className: com.lison.springsecurity.config.security-> SecurityConfig
 * @description:
 * @author: Lison
 * @createDate: 2024-06-01
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //1.配置基本认证方式
        http.authorizeRequests()
                //对任意请求都进行认证
                .anyRequest()
                .authenticated()
                .and()
                //开启basic认证
                .httpBasic();
    }
}

httpBasic()方法,就是用来开启基本认证的,而且默认采用的就是基本认证!

测试验证

访问自己的/hello接口,这时候我们就可以看到浏览器中弹出了一个登陆窗口。提示我们输入自己的用户名和密码 lison/123456,认证成功后,即可访问自己的web接口。

Basic认证详解

基本认证过程

此时的响应码为401,什么情况下会导致401状态码?

在这里插入图片描述

根据401和以上响应头信息,浏览器会弹出一个对话框,要求输入 用户名/密码,Basic认证会将其拼接成 “用户名:密码” 格式,中间是一个冒号,并利用Base64编码成加密字符串xxx;然后在请求头中附加 Authorization: Basic xxx 信息,发送给后台认证;后台需要利用Base64来进行解码xxx,得到用户名和密码,再校验 用户名:密码 信息。

  • 如果认证错误,浏览器会保持弹框;
  • 如果认证成功,浏览器会缓存有效的Base64编码,在之后的请求中,浏览器都会在请求头中添加该有效编码。
注销Basic认证

在成功认证之后,Basic认证会把Authorization认证信息缓存在浏览器中一段时间,之后每次请求接口时都会自动带上,所以直到 用户关闭浏览器才会销毁认证信息,也就是说我们无法在服务端进行有效的注销。

不过在请求注销时,前端也可以手动 在请求头配置一个错误的Authorization,或者在浏览器的命令行执行 document.execuCommand(“ClearAuthenticationCache”)方法 来清空认证信息,但该方式对Chrome浏览器无效。我们在调试基本认证时,可以直接开启无痕模式,避免很多因为缓存造成的问题。

Form表单认证

表单认证简介

对于表单认证,其实在SpringBoot开发环境中,只要我们添加了Spring Security的依赖包,就会自动实现表单认证。在WebSecurityConfigurerAdapter类的config(HttpSecurity http)方法中,可以看到如下默认实现。

在这里插入图片描述

在SpringBoot环境中,默认支持的就是表单认证方式。

表单认证效果

第一个Spring Security项目中实现的效果,其实就是表单认证。每次我们在访问某个Web接口之前,都会重定向到一个Security自带的login登录页面上,这个登录页面,就是表单认证的效果

在这里插入图片描述

表单认证中的预置url和页面

这时候有的小伙伴可能就会很好奇,为什么表单认证会有以上效果?这是因为在默认的formLogin配置中,自动配置了一些url和页面:

  • /login(get): get请求时会跳转到这个页面,只要我们访问任意一个需要认证的请求时,都会跳转到这个登录界面。
  • /login(post): post请求时会触发这个接口,在登录页面点击登录时,默认的登录页面表单中的action就是关联这个login接口。
  • /login?error: 当用户名或密码错误时,会跳转到该页面。
  • /: 登录成功后,默认跳转到该页面,如果配置了index.html页面,则 ”/“ 会重定向到index.html页面,当然这个页面要由我们自己实现。
  • /logout: 注销页面。
  • /login?logout: 注销成功后跳转到的页面。

由此可见,SpringSecurity默认有两个login,即登录页面和登录接口的地址都是 /login:

  • GET http://localhost:8080/login
  • POST http://localhost:8080/login

如果是GET 请求,表示你想访问登录页面;如果是 POST 请求,表示你想提交登录数据。

对于这几个URL接口,我们简单了解即可。

自定义表单认证配置

创建SecurityConfig配置类

我们先编写一个类,继承自WebSecurityConfigurerAdapter父类,该类的作用如下:

  • 验证所有请求;
  • 允许用户使用表达登录进行身份验证;
  • 允许用户使用Http基本认证
package com.lison.springsecurity.config.security;

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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * @className: com.lison.springsecurity.config.security-> SecurityConfig
 * @description:
 * @author: Lison
 * @createDate: 2024-06-01
 */

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置表单认证方式
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                //开启表单认证
                .formLogin();
    }
}

SecurityConfig类上添加@EnableWebSecurity注解后,会自动被Spring发现并注册。在configure()方法中,我执行了formLogin()方法,该方法的功能就是开启表单认证。

测试应用

启动项目,访问我们定义的/hello接口时,首先会重定向到/login页面。输入自己配置的用户名和密码后,才可以正常访问/hello接口。

在这里插入图片描述

当认证成功后,内部再次发生了302重定向:可见从/login接口重定向到了/hello接口。

自定义表单认证的登录界面

Spring Security一个特点就在于可以高度自定义,灵活配置,可以自定义一个登录页面。

服务端代码定义
package com.lison.springsecurity.config.security;

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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * @className: com.lison.springsecurity.config.security-> SecurityConfig
 * @description:
 * @author: Lison
 * @createDate: 2024-06-01
 */

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 用来定义哪些请求需要忽略安全控制,哪些请求必须接受安全控制;还可以在合适的时候清除SecurityContext以避免内存泄漏,
     * 同时也可以用来定义请求防火墙和请求拒绝处理器,另外我们开启Spring Security Debug模式也是这里配置的
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        //super.configure(web);
        web.ignoring()
                .antMatchers("/js/**", "/css/**", "/images/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //2.配置自定义的登录页面
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                //加载自定义的登录页面地址
                .loginPage("/myLogin.html")
                .permitAll()
                .and()
                //注意:需禁用crsf防护功能,否则登录不成功
                .csrf()
                .disable();
    }
}

1、WebSecurity执行流程

在这里插入图片描述

在configure(WebSecurity web)方法中,有个核心参数:WebSecurity类!在这个类里定义了一个securityFilterChainBuilders集合,可以同时管理多个SecurityFilterChain过滤器链,各位可以回顾我们学习Web基础时,关于过滤器的知识点,这些过滤器的执行是不是比Servlet更早?

当WebSecurity在执行时,会构建出一个名为 ”springSecurityFilterChain“Spring BeanFilterChainProxy代理类,它的作用是来 定义哪些请求可以忽略安全控制,哪些请求必须接受安全控制;以及在合适的时候 清除SecurityContext 以避免内存泄漏,同时也可以用来 定义请求防火墙和请求拒绝处理器,也可以在这里 开启Spring Security 的Debug模式

上面这一系列的Filter过滤器,我们就可以利用web.ignoring() 方法来配置想要忽略的静态资源 URL 地址,这样这些静态资源就可以不被拦截,从而可以被识别访问

2、HttpSecurity作用;

在这里插入图片描述

HttpSecurity用来构建包含一系列的过滤器链SecurityFilterChain,平常我们的配置就是围绕着这个SecurityFilterChain进行。

页面定义

首先自定义一个登陆页面,主要是编写html代码和css样式,其核心代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login Page</title>
    <style>
        body {
            background-color: #f2f2f2;
            font-family: Arial, sans-serif;
        }

        .container {
            max-width: 400px;
            margin: 0 auto;
            padding: 40px;
            background-color: #fff;
            border-radius: 5px;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
        }

        h2 {
            text-align: center;
            margin-bottom: 30px;
        }

        .form-group {
            margin-bottom: 20px;
        }

        label {
            display: block;
            font-weight: bold;
            margin-bottom: 5px;
        }

        input[type="text"],
        input[type="password"] {
            width: 100%;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 3px;
        }

        .btn {
            display: block;
            width: 100%;
            padding: 10px;
            background-color: #4caf50;
            color: #fff;
            font-weight: bold;
            text-align: center;
            text-decoration: none;
            border: none;
            border-radius: 3px;
            cursor: pointer;
        }

        .btn:hover {
            background-color: #45a049;
        }
    </style>
</head>
<body>
<div class="container">
    <h2>Login</h2>
    <form action="/myLogin.html" method="post">
        <div class="form-group">
            <label for="username">Username:</label>
            <input type="text" id="username" name="username" placeholder="Enter your username">
        </div>
        <div class="form-group">
            <label for="password">Password:</label>
            <input type="password" id="password" name="password" placeholder="Enter your password">
        </div>
        <button class="btn" type="submit">Login</button>
    </form>
</div>
</body>
</html>
测试应用

启动项目后,我们访问接口时,就会自动跳转到自己定义的/myLogin.html页面上,输入用户名和密码后,就可以成功访问自己的接口。
在这里插入图片描述

细化表单认证配置

修改表单认证页面中请求参数的名称,定义认证失败时的错误处理页面,处理退出登录时的操作等,这些都可以自定义配置,实现代码如下。

定义SecurityConfig类
package com.lison.springsecurity.config.security;

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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * @className: com.lison.springsecurity.config.security-> SecurityConfig
 * @description:
 * @author: Lison
 * @createDate: 2024-06-01
 */

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 用来定义哪些请求需要忽略安全控制,哪些请求必须接受安全控制;还可以在合适的时候清除SecurityContext以避免内存泄漏,
     * 同时也可以用来定义请求防火墙和请求拒绝处理器,另外我们开启Spring Security Debug模式也是这里配置的
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        //super.configure(web);
        web.ignoring()
                .antMatchers("/js/**", "/css/**", "/images/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //super.configure(http);

        //3.进一步配置自定义的登录页面   
        //拦截请求,创建FilterSecurityInterceptor 
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                //用and来表示配置过滤器结束,以便进行下一个过滤器的创建和配置
                .and()
                //设置表单登录,创建UsernamePasswordAuthenticationFilter
                .formLogin()
                .loginPage("/myLogin.html")
                .permitAll()
                //指登录成功后,是否始终跳转到登录成功url。它默认为false
                .defaultSuccessUrl("/index.html",true)
                //post登录接口,登录验证由系统实现
                .loginProcessingUrl("/login")
                //用户密码错误跳转接口
                .failureUrl("/error.html")
                //要认证的用户参数名,默认username
                .usernameParameter("username")
                //要认证的密码参数名,默认password
                .passwordParameter("password")
                .and()
                //配置注销
                .logout()
                //注销接口
                .logoutUrl("/logout")
                //注销成功后跳转到的接口
                .logoutSuccessUrl("/myLogin.html")
                .permitAll()
                //删除自定义的cookie
                .deleteCookies("myCookie")
                .and()
                //注意:需禁用crsf防护功能,否则登录不成功
                .csrf()
                .disable();
    }
}

自定义登录页面

登录页面也跟着修改一下,主要是把form表单中action的值修改掉为 login

<body>
<div class="container">
    <h2>Login</h2>
    <form action="/login" method="post">
        <div class="form-group">
            <label for="username">Username:</label>
            <input type="text" id="username" name="username" placeholder="Enter your username">
        </div>
        <div class="form-group">
            <label for="password">Password:</label>
            <input type="password" id="password" name="password" placeholder="Enter your password">
        </div>
        <button class="btn" type="submit">Login</button>
    </form>
</div>
</body>

注意:此时form表单中action的值,要写成”/login“,因为我们在配置类中通过“loginProcessingUrl(“/login”)”方法中做了明确的配置

定义错误处理页面

输入了错误的用户名和密码后,可以提供一个错误处理页面,当认证失败后跳转到这个页面即可

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1> ERROR 错误页面</h1>
</body>
</html>
测试验证

如果访问自己的接口,比如/hello,会先重定向到/myLogin.html页面,输入自己配置的用户名和密码,经过验证后,才会重定向到/index.html页面中,否则会重定向到我们配置的/error.html页面中。

1、重定向到/myLogin.html

访问资源时,会先重定向到自定义的登录页面。

在这里插入图片描述

2、重定向到/index.html

认证成功后,会重定向到index.html页面。

在这里插入图片描述

3、重定向到/error.html

认证失败后,会重定向到自定义的错误处理页面。

在这里插入图片描述

HTTP摘要认证

HTTP摘要认证简介

HTTP摘要认证和HTTP基本认证一样,也是在RFC2616中定义的一种认证方式,它的出现是为了弥补HTTP基本认证存在的安全隐患,但该认证方式也并不是很安全**。HTTP摘要认证会使用对通信双方来说都可知的口令进行校验,且最终以密文的形式来传输数据,所以相对于基本认证来说,稍微安全了一些。

HTTP摘要认证与基本认证类似,基于简单的“挑战-回应”模型。当发起一个未经认证的请求时,服务器会返回一个401回应,并给客户端返回与验证相关的参数,期待客户端依据这些参数继续做出回应,从而完成整个验证过程

HTTP摘要认证核心参数

服务端给客户端返回的验证相关参数如下:

  • username: 用户名。
  • password: 用户密码。
  • realm: 认证域,由服务器返回。
  • opaque: 透传字符串,客户端应原样返回。
  • method: 请求的方法。
  • nonce: 由服务器生成的随机字符串,包含过期时间(默认过期时间300s)和密钥。
  • nc: 即nonce-count,指请求的次数,用于计数,防止重放攻击。qop被指定时,nc也必须被指定。
  • cnonce: 客户端发给服务器的随机字符串,qop被指定时,cnonce也必须被指定。
  • qop: 保护级别,客户端根据此参数指定摘要算法。若取值为 auth,则只进行身份验证;若取值为auth-int,则还需要校验内容完整性,默认的qop为auth。
  • uri: 请求的uri。
  • response: 客户端根据算法算出的摘要值,这个算法取决于qop。
  • algorithm: 摘要算法,目前仅支持MD5。
  • entity-body: 页面实体,非消息实体,仅在auth-int中支持。

通常服务器端返回的数据包括realm、opaque、nonce、qop等字段,如果客户端需要做出验证回应,就必须按照一定的算法得到一些新的数据并一起返回**。在以上各种参数中,对服务器而言,最重要的字段是nonce;对客户端而言,最重要的字段是response**。

摘要认证代码实现

编写测试接口
package com.lison.springsecurity.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @className: com.lison.springsecurity.controller-> IndexController
 * @description:
 * @author: Lison
 * @createDate: 2024-06-01
 */
@RestController
public class IndexController {

    @GetMapping("/admin/hello")
    public String helloAdmin() {

        return "hello, admin";
    }

    @GetMapping("/user/hello")
    public String helloUser() {

        return "hello, user";
    }

    @GetMapping("/visitor/hello")
    public String helloVisitor() {

        return "hello, visitor";
    }

}
创建SecurityConfig配置类

这里比较重要的是配置摘要认证入口端点DigestAuthenticationEntryPoint

package com.lison.springsecurity.config.security;

import com.lison.springsecurity.service.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.DigestAuthenticationFilter;

/**
 * @className: com.lison.springsecurity.config.security-> SecurityConfig
 * @description:
 * @author: Lison
 * @createDate: 2024-06-01
 */
@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DigestAuthenticationEntryPoint digestAuthenticationEntryPoint;

    @Autowired
    private MyUserDetailsService userDetailsService;

    //配置认证入口端点,主要是设置认证参数信息
    @Bean
    public DigestAuthenticationEntryPoint digestAuthenticationEntryPoint(){
        DigestAuthenticationEntryPoint point=new DigestAuthenticationEntryPoint();
        point.setKey("Security Demos");
        point.setRealmName("lison");
        point.setNonceValiditySeconds(500);
        return point;
    }

    public DigestAuthenticationFilter digestAuthenticationFilter(){
        DigestAuthenticationFilter filter=new DigestAuthenticationFilter();
        filter.setAuthenticationEntryPoint(digestAuthenticationEntryPoint);
        filter.setUserDetailsService(userDetailsService);
        return filter;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**")
                .hasRole("ADMIN")
                .antMatchers("/user/**")
                .hasRole("USER")
                .antMatchers("/visitor/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .csrf()
                .disable()
                //当未认证时访问某些资源,则由该认证入口类来处理.
                .exceptionHandling()
                .authenticationEntryPoint(digestAuthenticationEntryPoint)
                .and()
                //添加自定义过滤器到过滤器链中
                .addFilter(digestAuthenticationFilter());
    }

}

测试接口

访问一下需要认证的接口/admin/hello接口,这时候会发现浏览器弹出了一个用户名密码的认证窗口。然后我们在浏览器中可以看到HTTP摘要认证信息,realm是我们自定义的“lison”,qop是默认的“auth”方式。

在这里插入图片描述

由此可见,此时摘要认证的方式已经生效。

HTTP摘要认证弊端

HTTP摘要认证与HTTP基本认证一样,都是基于HTTP层面的认证方式,也不能使用Session对象,因而也不支持Remember-Me功能。该方式虽然解决了HTTP基本认证中以明文传输密码的问题,但并未解决密码明文存储的问题,所以依然有安全隐患,所以在开发中,摘要认证的方式也不怎么使用。

以上三种认证方式,我们需要重点掌握表单认证;

前后端分离时的安全处理方案

前后端分离简介

企业开发中,前后端分离已成为互联网项目开发的业界标准方式,其核心思想就是前端HTML页面通过AJAX,调用后端的RESTFUL API接口,并使用JSON数据进行交互****。

该方式可以有效的在前后端项目之间进行解耦,并且前后端分离会为以后的大型分布式架构、弹性计算架构、微服务架构、多端化服务(多种客户端,例如 浏览器,车载终端,安卓,IOS等)打下坚实的基础。

认证处理时的相关API

页面跳转的相关API

1、登录成功时的跳转API

  • defaultSuccessUrl
  • successForwardUrl

2、登录失败时的跳转API

  • failureUrl()
  • failureForwardUrl()
返回JSON格式的处理器

在前后端分离模式下,既然后端没有页面,页面都在前端,那就可以考虑使用JSON来进行信息交互了,我们把认证成功或认证失败的信息,以JSON的格式传递给前端,由前端来决定到底该往哪个页面跳转;

我们要返回JSON格式的信息,有如下相关方法:

  • successHandler()
  • failureHandler()
  • logoutSuccessHandler()
  • authenticationEntryPoint()

认证成功时的处理方案

相关的方法及其核心参数,即successHandler()和onAuthenticationSuccess参数。

1. successHandler()方法

successHandler()方法的功能十分强大,甚至也囊括了 defaultSuccessUrl()和 successForwardUrl() 的功能。

successHandler()方法的参数是一个 AuthenticationSuccessHandler 对象,这个对象中我们要实现的方法是 onAuthenticationSuccess()。

2. onAuthenticationSuccess参数

onAuthenticationSuccess() 方法中有三个参数,分别是:

  • HttpServletRequest: 利用该参数我们可以实现服务端的跳转;
  • HttpServletResponse: 利用该参数我们可以做客户端的跳转,也可以返回 JSON 数据;
  • Authentication: 这个参数则保存了我们刚刚登录成功的用户信息。
定义SecurityAuthenticationSuccessHandler类

r认证成功后需要处理的类,要实现AuthenticationSuccessHandler 接口

/**
 * 处理登录成功时的业务逻辑
 */
public class SecurityAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    /**
     * Authentication:携带登录的用户名及角色等信息
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
        //直接输出json格式的响应信息
        Object principal = authentication.getPrincipal();
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        //以json格式对外输出身份信息
        out.write(new ObjectMapper().writeValueAsString(principal));
        out.flush();
        out.close();
    }
}
配置successHandler

在SecurityConfig配置类中,调用successHandler()方法,把前面定义的SecurityAuthenticationSuccessHandler类关联进来

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
                http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .permitAll()
                //认证成功时的处理器
                .successHandler(new SecurityAuthenticationSuccessHandler())
                .and()
                .csrf()
                .disable();
    }

}
验证结果

我们进行登录验证,在认证成功后,就可以看到登录成功的用户信息是通过 JSON 返回到前端的,如下图所示:

在这里插入图片描述

Spring Security会把认证的用户信息以JSON格式展示出来,比如我们的用户名、密码、角色等信息。

认证失败时的处理方案

相关的API方法及参数。

failureHandler()

failureHandler()方法的参数是一个 AuthenticationFailureHandler 对象,这个对象中我们要实现的方法是 onAuthenticationFailure()。

onAuthenticationFailure()方法有三个参数,分别是:

  • HttpServletRequest: 利用该参数我们可以实现服务端的跳转;
  • HttpServletResponse: 利用该参数我们可以做客户端的跳转,也可以返回 JSON 数据;
  • AuthenticationException: 这个参数则保存了登录失败的原因。
代码实现

同样的,我们也要编写一个类SecurityAuthenticationFailureHandler,实现AuthenticationFailureHandler接口,来专门处理认证失败时的返回结果。

/**
 * 处理登录失败时的业务逻辑
 */
public class SecurityAuthenticationFailureHandler  implements AuthenticationFailureHandler {

    /**
     * AuthenticationException:异常信息
     */
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
        //直接输出json格式的响应信息
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.write(e.getMessage());
        out.flush();
        out.close();
    }
}
配置failureHandler

接着我们在SecurityConfig配置类中,调用failureHandler()方法来关联上面定义的SecurityAuthenticationFailureHandler类对象。

核心代码如下:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
                http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .permitAll()
                //认证成功时的处理器
                .successHandler(new SecurityAuthenticationSuccessHandler())
                //认证失败时的处理器
                .failureHandler(new SecurityAuthenticationFailureHandler())
                .and()
                .csrf()
                .disable();
    }

}
验证结果

配置完成后,我们再去登录,在认证失败时,就可以看到登录失败的用户信息通过 JSON 返回到前端了

在这里插入图片描述

退出登录时的处理方案

认证成功和认证失败后的处理方案后,退出登录后处理方案

1. logoutSuccessHandler()

负责退出登录的方法是logoutSuccessHandler(),这个方法中需要一个参数LogoutSuccessHandler;在LogoutSuccessHandler类中有一个方法 onLogoutSuccess(),该方法中的参数与登录成功时的参数一样。

2. 定义SecurityLogoutSuccessHandler类

我们先来定义一个SecurityLogoutSuccessHandler类,实现LogoutSuccessHandler接口,在这里负责输出退出登录时的JSON结果。

public class SecurityLogoutSuccessHandler implements LogoutSuccessHandler {

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.write("注销成功");
        out.flush();
        out.close();
    }

}
配置logoutSuccessHandler

然后我们在SecurityConfig配置类中,调用logoutSuccessHandler()方法来关联上面定义的SecurityLogoutSuccessHandler对象。

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .permitAll()
                //认证成功时的处理器
                .successHandler(new SecurityAuthenticationSuccessHandler())
                //认证失败时的处理器
                .failureHandler(new SecurityAuthenticationFailureHandler())
                .and().logout()
                //退出登录时的处理器
                .logoutSuccessHandler(new SecurityLogoutSuccessHandler())
                .and()
                .csrf()
                .disable();
    }

}
验证结果

配置完成后,我们去访问/logout接口,退出登录成功,会有如下所示结果:

在这里插入图片描述

未认证时的处理方案

1. authenticationEntryPoint()

未认证时,同样有个专门的方法来处理,即authenticationEntryPoint()方法,这个方法中需要一个参数LoginUrlAuthenticationEntryPoint,在LoginUrlAuthenticationEntryPoint类中有一个方法 commence()。

2. 定义SecurityAuthenticationEntryPoint类

我们定义一个SecurityAuthenticationEntryPoint类,实现AuthenticationEntryPoint接口,在这里负责输出未认证时的JSON结果。

/**
 * @className: com.lison.springsecurity.config.security.handler-> SecurityAuthenticationEntryPoint
 * @description: 处理未登录认证时的响应信息
 * @author: Lison
 * @createDate: 2024-06-01
 */
public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.write("尚未登录,请先登录");
        out.flush();
        out.close();
    }

}
配置authenticationEntryPoint

然后我们在SecurityConfig配置类中,调用authenticationEntryPoint()方法来关联上面定义的SecurityAuthenticationEntryPoint对象。

验证结果

配置完成后,我们在未登录时,直接去访问项目中的某个接口,就会看到未登录时返回的JSON信息,如下图所示:

在这里插入图片描述

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

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

相关文章

2-9 存储管理

9.1 传统磁盘管理 1.添加磁盘 在关闭虚拟机情况下添加磁盘(SATA) 别忘记点确定&#xff01;&#xff01;&#xff01; 重启虚拟机&#xff0c;并查看磁盘情况 [rootlocalhost ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sda 8:0 0 20G…

word及Excel常见功能使用

最近一直在整理需规文档及表格&#xff0c;Word及Excel需要熟练使用。 Word文档 清除复制过来的样式 当复制文字时&#xff0c;一般会带着字体样式&#xff0c;此时可选中该文字 并使用 ctrlshiftN 快捷键进行清除。 批注 插入->批注&#xff0c;选中文本 点击“批注”…

【Linux 27】HTTP 协议中的 cookie 和 session

文章目录 &#x1f308;一、Cookie 的相关概念⭐ 1. Cookie 的概念⭐ 2. Cookie 的工作原理⭐ 3. Cookie 的分类⭐ 4. Cookie 的用途⭐ 5. Cookie 设置的基本格式⭐ 6. Cookie 设置时的注意事项⭐ 7. Cookie 的生命周期⭐ 8. Cookie 的安全性问题 &#x1f308; 二、Session 的…

SQL用一个字段查询出数据的交集

出个SQL查询的题吧&#xff0c;有兴趣的可以看看 1、问题 下面有一份数据&#xff08;图1&#xff09;&#xff0c;由两部分组成&#xff1a;分析数据和基准数据 分析数据标识列为1&#xff0c;基准数据标识列为1&#xff0c;两字段0,1互斥 要求&#xff1a;按找出两部分数据…

后端java——如何为你的网页设置一个验证码

目录 1、工具的准备 2.基本方法 3.实现类 4.实践 HTML文件&#xff1a; Java文件1:创建验证码 Java文件2:验证验证码 本文通过HUTOOL实现&#xff1a;Hutool参考文档Hutool&#xff0c;Java工具集https://hutool.cn/docs/#/ 1、工具的准备 如果我们通过hutool来实现这个…

【Python单元测试】pytest框架单元测试 配置 命令行操作 测试报告 覆盖率

单元测试&#xff08;unit test&#xff09;&#xff0c;简称UT。本文将介绍在Python项目中&#xff0c;pytest测试框架的安装&#xff0c;配置&#xff0c;执行&#xff0c;测试报告与覆盖率 pytest简介 pytest是一款流行的&#xff0c;简单易上手的单元测试框架&#xff0c;…

HTMLCSS:呈现的3D树之美

效果演示 这段代码通过HTML和CSS创建了一个具有3D效果的树的图形&#xff0c;包括分支、树干和阴影&#xff0c;通过自定义属性和复杂的变换实现了较为逼真的立体效果。 HTML <div class"container"><div class"tree"><div class"…

练习LabVIEW第三十八题

学习目标&#xff1a; 刚学了LabVIEW&#xff0c;在网上找了些题&#xff0c;练习一下LabVIEW&#xff0c;有不对不好不足的地方欢迎指正&#xff01; 第三十八题&#xff1a; 创建一个VI&#xff0c;实现对按钮状态的指示和按钮“按下”持续时间简单计算功能&#xff0c;按…

HomeAssistant自定义组件学习-【二】

#要说的话# 前面把中盛科技的控制器组件写完了。稍稍熟悉了一些HA&#xff0c;现在准备写窗帘控制组件&#xff0c;构想的东西会比较多&#xff0c;估计有些难度&#xff0c;过程会比较长&#xff0c;边写边记录吧&#xff01; #设备和场景环境# 使用的是Novo的电机&#xf…

Linux脚本数组与字符串

文章目录 打印数组与长度数组遍历数组赋值下标索引访问切片追加()删除关联数组(像map)字符串字符拼接截取子串字符串长度字符串替换模式匹配截取分割字符串大小写转换 打印数组与长度 ${arrayName[*]} 打印数组${arrayName[]} 打印数组${#arrayName[*]} 打印数组长度${#arrayN…

闯关leetcode——3289. The Two Sneaky Numbers of Digitville

大纲 题目地址内容 解题代码地址 题目 地址 https://leetcode.com/problems/the-two-sneaky-numbers-of-digitville/description/ 内容 In the town of Digitville, there was a list of numbers called nums containing integers from 0 to n - 1. Each number was suppos…

#Jest进阶知识:整合 webpack 综合练习

这一小节&#xff0c;我们来做一个综合的练习&#xff0c;该练习会整合&#xff1a; typescriptwebpackjest 准备工作 首先创建项目目录&#xff0c;通过 npm init -y 进行初始化。 整个项目我们打算使用 typescript 进行开发&#xff0c;因此需要安装 typescript npm i t…

MATLAB——矩阵操作

内容源于b站清风数学建模 数学建模清风老师《MATLAB教程新手入门篇》https://www.bilibili.com/video/BV1dN4y1Q7Kt/ 目录 1.MATLAB中的向量 1.1向量创建方法 1.2向量元素的引用 1.3向量元素修改和删除 2.MATLAB矩阵操作 2.1矩阵创建方法 2.2矩阵元素的引用 2.3矩阵…

原生鸿蒙应用市场开发者服务的技术解析:从集成到应用发布的完整体验

文章目录 引言一、鸿蒙原生应用的高效开发二、用户隐私保护&#xff1a;安全访问管理三、开发者实用工具&#xff1a;应用分析与A/B测试四、应用审核与分发&#xff1a;快速上线4.1 应用加密&#xff1a;保护代码安全4.2 自动化测试与检测前移&#xff1a;提升应用质量 五、结语…

基于SSM+微信小程序的社团登录管理系统(社团1)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 2、项目技术 3、开发环境 4、功能介绍 1、项目介绍 基于SSM微信小程序的社团登录管理系统实现了管理员及社团、用户。 1、管理员实现了首页、用户管理、社团管理、社团信息管理、社…

虚拟化环境中的精简版 Android 操作系统 Microdroid

随着移动设备的普及和应用场景的多样化&#xff0c;安全性和隐私保护成为了移动操作系统的重要课题。Google推出的Microdroid&#xff0c;是一个专为虚拟化环境设计的精简版Android操作系统&#xff0c;旨在提供一个安全、隔离的执行环境。本文将详细介绍Microdroid的架构、功能…

手动搭建 Java Web 环境

操作场景 本文档介绍如何在 Linux 操作系统的腾讯云云服务器&#xff08;CVM&#xff09;上手动搭建 Java Web 环境。 进行手动搭建 Java Web 环境&#xff0c;您需要熟悉 Linux 命令&#xff0c;例如 CentOS 环境下通过 YUM 安装软件 等常用命令&#xff0c;并对所安装软件使…

WPF+MVVM案例实战与特效(二十四)- 粒子字体效果实现

文章目录 1、案例效果2、案例实现1、文件创建2.代码实现3、界面与功能代码3、总结1、案例效果 提示:这里可以添加本文要记录的大概内容: 2、案例实现 1、文件创建 打开 Wpf_Examples 项目,在 Views 文件夹下创建窗体界面 ParticleWindow.xaml,在 Models 文件夹下创建粒子…

「Mac畅玩鸿蒙与硬件18」鸿蒙UI组件篇8 - 高级动画效果与缓动控制

高级动画可以显著提升用户体验&#xff0c;为应用界面带来更流畅的视觉效果。本篇将深入介绍鸿蒙框架的高级动画&#xff0c;包括弹性动画、透明度渐变和旋转缩放组合动画等示例。 关键词 高级动画弹性缓动自动动画缓动曲线 一、Animation 组件的高级缓动曲线 缓动曲线&#…

Golang--数组、切片、映射

1、数组 1.1 数组类型 var 数组名 [数组大小]数据类型 package main import "fmt"func main(){//1、定义一个数组var arr1 [5]intarr1[0] 100arr1[1] 200fmt.Println(arr1) //[100 200 0 0 0] } 1.2 数组的初始化方式 package main import "fmt" func …