Spring Security 是一个强大且灵活的安全框架,提供了身份验证(认证)和授权(授权)功能。下面我们将详细介绍 Spring Security 的认证功能流程,并提供自定义实现登录接口的示例,包括自定义认证过滤器和登出功能。

一、Spring Security 认证流程的深入分析

Spring Security 的认证流程是多层次的,涉及多个组件的协作。以下是每个步骤的深入分析:

  1. 请求拦截

    当用户请求一个受保护的资源时,Spring Security 会使用过滤器链来处理请求。FilterChainProxy 是 Spring Security 的核心过滤器,它将请求传递给注册的过滤器链。

  2. 认证过滤器

    默认情况下,UsernamePasswordAuthenticationFilter 会被用作处理用户名和密码的认证。它从请求中提取认证信息,通常是通过 POST 请求的表单数据传递。

    关键方法 attemptAuthentication 中,使用 AuthenticationManager 来处理认证请求。AuthenticationManager 负责委托认证给具体的认证提供者。

  3. 用户详情服务(UserDetailsService)

    认证过程中的一个重要步骤是从数据源中加载用户信息。UserDetailsService 接口提供了一个 loadUserByUsername 方法,负责根据用户名加载用户详情。

    通常,用户信息存储在数据库中,UserDetails 对象将包含用户名、密码和权限信息。Spring Security 提供了多种 UserDetailsService 的实现,开发者也可以自定义实现。

  4. 密码验证

    一旦获取到用户详情,接下来的步骤是验证密码。使用 PasswordEncoder 对用户输入的密码与存储在数据库中的密码进行比对。

    Spring Security 支持多种加密算法(如 BCrypt、PBKDF2、SCrypt),并允许开发者自定义密码编码器。

  5. 成功和失败处理

    认证成功后,successfulAuthentication 方法被调用。在此方法中,开发者可以实现自定义的成功逻辑,例如返回 JWT 令牌、设置用户会话等。

    如果认证失败,unsuccessfulAuthentication 方法会被调用,可以根据需要返回错误消息或重定向到登录页面。

二、自定义登录接口的实现

1. 自定义认证过滤器的设计

创建自定义认证过滤器时,需要继承 UsernamePasswordAuthenticationFilter 并重写相应的方法。以下是详细实现:

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import com.fasterxml.jackson.databind.ObjectMapper; public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private final AuthenticationManager authenticationManager; public CustomAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
} @Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username = request.getParameter("username");
String password = request.getParameter("password"); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
return authenticationManager.authenticate(authenticationToken);
} @Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
// 可以返回用户信息或 JWT 令牌
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_OK);
ObjectMapper objectMapper = new ObjectMapper();
String token = "some_generated_jwt"; // 实际上要生成 JWT
response.getWriter().write(objectMapper.writeValueAsString("token: " + token));
} @Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("{\"error\": \"" + failed.getMessage() + "\"}");
}
}

2. 配置 Spring Security 的详细步骤

在配置类中,我们将添加自定义过滤器并设置用户存储方式。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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; @Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 配置用户存储方式
auth.inMemoryAuthentication()
.withUser("user").password("{noop}password").roles("USER")
.and()
.withUser("admin").password("{noop}admin").roles("ADMIN");
} @Override
protected void configure(HttpSecurity http) throws Exception {
CustomAuthenticationFilter customFilter = new CustomAuthenticationFilter(authenticationManagerBean());
customFilter.setFilterProcessesUrl("/login"); // 自定义登录路径 http.csrf().disable()
.authorizeRequests()
.antMatchers("/login").permitAll() // 允许访问登录接口
.anyRequest().authenticated() // 其他请求需要认证
.and()
.addFilter(customFilter) // 添加自定义认证过滤器
.logout()
.logoutUrl("/logout") // 自定义登出路径
.logoutSuccessUrl("/login?logout") // 登出成功后的重定向地址
.invalidateHttpSession(true) // 登出时使 HTTP 会话失效
.deleteCookies("JSESSIONID"); // 删除指定的 Cookie
} @Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}

三、登出功能的实现

在 Spring Security 中,登出功能非常简单。配置登出路径和成功重定向即可。在上述配置中,我们已经为登出功能添加了以下配置:

  • logoutUrl("/logout"):指定登出的 URL。
  • logoutSuccessUrl("/login?logout"):登出成功后的重定向 URL。
  • invalidateHttpSession(true):登出时使 HTTP 会话失效。
  • deleteCookies("JSESSIONID"):在登出时删除指定的 Cookie。

四、设计考虑与常见问题

  1. 设计考虑

    灵活性:自定义认证过滤器允许我们实现不同的认证逻辑,如 OAuth2、JWT 等,保持系统的灵活性。

    安全性:在实现过程中,确保敏感信息(如密码)不被明文传输和存储,推荐使用 HTTPS 和合适的密码加密方式。

    错误处理:对失败的认证提供明确的反馈,方便用户理解问题所在,提升用户体验。

  2. 常见问题

    跨域问题:在前后端分离的应用中,登录接口可能会遇到跨域请求问题。可以通过设置 CORS 策略来解决。

    状态管理:如果使用 JWT 进行认证,需注意如何管理状态和续期机制。

    并发登录问题:需要考虑多个设备或浏览器同时登录的情况,可能需要实现会话管理。

一文彻底搞定Spring Security 认证,实现登陆登出功能的更多相关文章

  1. 【项目实践】一文带你搞定Spring Security + JWT

    以项目驱动学习,以实践检验真知 前言 关于认证和授权,R之前已经写了两篇文章: [项目实践]在用安全框架前,我想先让你手撸一个登陆认证 [项目实践]一文带你搞定页面权限.按钮权限以及数据权限 在这两篇 ...

  2. spring security 认证源码跟踪

    spring security 认证源码跟踪 ​ 在跟踪认证源码之前,我们先根据官网说明一下security的内部原理,主要是依据一系列的filter来实现,大家可以根据https://docs.sp ...

  3. Spring Security认证配置(三)

    学习本章之前,可以先了解下上篇Spring Security认证配置(二) 本篇想要达到这样几个目的: 1.登录成功处理 2.登录失败处理 3.调用方自定义登录后处理类型 具体配置代码如下: spri ...

  4. 3步轻松搞定Spring Boot缓存

    作者:谭朝红 前言 本次内容主要介绍基于Ehcache 3.0来快速实现Spring Boot应用程序的数据缓存功能.在Spring Boot应用程序中,我们可以通过Spring Caching来快速 ...

  5. 用Hopper搞定Mac迅雷的会员以及离线下载功能

    转自 用Hopper搞定Mac迅雷的会员以及离线下载功能 先定位Mac迅雷的可执行文件 snakeninnys-iMac:~ snakeninny$ ls /Applications/Thunder. ...

  6. spring security+freemarker获取登陆用户的信息

    spring security+freemarker获取登陆用户的信息 目标页面之间获取 ${Session.SPRING_SECURITY_CONTEXT.authentication.princi ...

  7. 一文搞定Spring Boot + Vue 项目在Linux Mysql环境的部署(强烈建议收藏)

    本文介绍Spring Boot.Vue .Vue Element编写的项目,在Linux下的部署,系统采用Mysql数据库.按照本文进行项目部署,不迷路. 1. 前言 典型的软件开发,经过" ...

  8. Spring Security 入门(1-4-2)Spring Security - 认证过程之AuthenticationProvider的扩展补充说明

    1.用户信息从数据库获取 通常我们的用户信息都不会向第一节示例中那样简单的写在配置文件中,而是从其它存储位置获取,比如数据库.根据之前的介绍我们知道用户信息是通过 UserDetailsService ...

  9. Spring Security 入门(1-4-1)Spring Security - 认证过程

    理解时可结合一下这位老兄的文章:http://www.importnew.com/20612.html 1.Spring Security的认证过程 1.1.登录过程 - 如果用户直接访问登录页面 用 ...

  10. Authentication讲解(Spring security认证)

    标准认证过程: 1.用户使用username和password登录 2.系统验证这个password对于该username是正确的 3.假设第二步验证成功,获取该用户的上下文信息(如他的角色列表) 4 ...

随机推荐

  1. 当 Spring 循环依赖碰上 Aysnc,调试过程中出现 BeanCurrentlyInCreationException,有点意思

    开心一刻 前两天有个女生加我,我同意了 第一天,她和我聊文学,聊理想,聊篮球,聊小猫小狗 第二天,她和我说要看我腹肌 吓我一跳,我反手就删除拉黑,我特喵一肚子的肥肉,哪来的腹肌! 循环依赖 关于 Sp ...

  2. Java学习笔记2--JDK的安装和配置

    一.进入oracle官网,下载jdk oracle官网:Oracle | Cloud Applications and Cloud Platform ps:不同的浏览器,可能进入oracle官网,会只 ...

  3. Structures, unions, enumerations, and bitfields in ARM C and C++

    Structures, unions, enumerations, and bitfields in ARM C and C++ Non-Confidential   ARM DUI0375E ARM ...

  4. GPL前世今生

    从事Linux开发的朋友一定都听过GPL,那么到底什么是GPL呢?他有什么作用呢?本文给大家做详细讲解. 一.GNU/GPL 在讲解GPL之前,我们必须先了解什么是GNU? 1. 什么是GNU GNU ...

  5. python3实现url全编码/解码

    最近在学习SQL注入,绕过方法中有编码注入绕过,需要将关键词进行全编码,百度了一下没有找到全编码工具,所有的编码工具里"and"编码完还是"and",于是查了一 ...

  6. IVIEW 评分组件 Rate 介绍

    1. 首先,评分允许评半颗星,使用 allow-half <template> <Rate allow-half v-model="valueHalf" /> ...

  7. php自定义函数访问其他地方定义的变量

    新捣鼓php,很多地方有.net的思维好难改过来. 在Connection/config.php 自定义了数据库连接字符串 然后在外部页面,自定义了一个function,对数据库进行操作 然后死活都给 ...

  8. Java 1.8 Stream流原理与用法总结

    一.接口设计 从Java1.8开始提出了Stream流的概念,侧重对于源数据计算能力的封装,并且支持序列与并行两种操作方式:依旧先看核心接口的设计: BaseStream:基础接口,声明了流管理的核心 ...

  9. 低代码 + BI 数字化转型如何助力制造业供应链协同?

    引言 在当今快速变化的商业环境中,制造业面临着前所未有的挑战和机遇.全球化竞争.消费者需求的快速变化.技术创新的加速以及不断增加的成本压力,都要求制造企业不断提高其供应链的效率和灵活性.供应链协同作为 ...

  10. 算法学习-CDQ分治

    对于二维偏序,为普通的求逆序对,只需要先排序一遍,然后树状数组或双指针即可 而三位偏序甚至更高,则需要用 CDQ 分治,简单来说,就是将树状数组和双指针结合 操作步骤如下: 1.开始将数组按第一维排序 ...