SpringSecurity框架(入门)

简介:

Spring Security 是一个用于构建安全的 Java 应用程序的框架,尤其适用于基于Spring的应用程序。它提供了全面的安全控制,从认证(Authentication)到授权(Authorization),以及一系列的中间件功能,如CSRF防护、会话管理等。以下是Spring Security的核心概念和使用方式的详细说明:

What: Spring Security 是什么?

Spring Security 是一套用于保护Web应用程序免受未经授权访问的框架。它提供了以下核心功能:

  • 认证:验证用户身份,例如通过用户名和密码登录。

  • 授权:控制对资源的访问,例如,只有管理员才能访问某些页面。

  • 会话管理:确保会话安全,防止会话劫持和固定会话攻击。

  • CSRF防护:防止跨站请求伪造(Cross-Site Request Forgery)。

  • 过滤器链 :通过一系列的过滤器来处理HTTP请求,执行安全相关的操作。(我们主要就是写自定义的过滤器)

Why: 为什么使用Spring Security?

  • 声明式安全:通过注解或配置文件轻松声明谁可以访问什么资源,无需手动编写安全检查代码。

  • 高度可定制:可以根据需求调整认证和授权逻辑,支持多种认证和授权机制。

  • 与Spring的集成:无缝集成Spring框架,与Spring Boot、Spring MVC等组件配合良好。

  • 安全功能丰富:提供多种安全特性,覆盖了Web应用安全的多个方面。

How: 如何使用Spring Security?

使用Spring Security通常包括以下步骤:

  • 依赖配置:在项目中引入Spring Security的依赖,如果是Spring Boot项目,可以通过添加相应的starter依赖。

  • 配置安全:创建一个WebSecurityConfigurerAdapter的子类,重写其配置方法来定义安全规则,例如哪些URL需要保护,使用哪种认证方式等。

SpringSecurity认证管理流程

下图是一个简单的Security认证管理流程

快速入门实践

接下来,我们创建一个小的Module感受下SpringSecurity在登录情景下的使用

创建Module

  1. 在当前最外层根目录下,右键,选择 "New"-->"Module"--"Spring Initializer"-->选择"设置"->将初始化服务器地址设置为https://start.aliyun.com国内阿里云
  1. 按照下图所示的内容创建 "securityDemo"
  1. 添加六个依赖

删除原有的demos目录及文件

如图所示修改application.properties

   # 应用服务 WEB 访问端口
   server.port=8080

   # 数据库连接信息
   spring.datasource.url=jdbc:mysql://localhost:3306/ivos?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
   spring.datasource.username=root
   spring.datasource.password=root

启动项目,控制台会出现如下图所示的一个密码

复制这个密码,马上要用

访问登录页面

SpringSecurity自己提供了一个登录页,我们可以直接通过 http://localhost:8080/login 访问

  • 默认用户名 user

  • 密码为之前控制台输出的password 1854cb7d-e2ef-4d84-a4f7-df2e18855a24

注意:这个密码每个人不一样,而且这个登录页打开比较慢,需要等一下

认证流程拆分

结合上面的这个登录认证流程,我们来继续补充完善完整的流程,Spring Security 的流程控制涉及了一系列的过滤器,这些过滤器构成了大家俗称的"安全过滤器链"。完整流程是这样的:

  1. 用户尝试访问受Spring Security保护下的URL地址

  2. 访问此URL需要检验有没有认证,Spring Security会检查该请求是否允许放行

  3. 如果 不允许放行

    1. 我们指定的过滤器会返回拒绝访问的状态码,一般为401未授权(SC_UNAUTHORIZED)与403拒绝访问(SC_FORBIDDEN)

    2. 如果在没有登录的情况下,所有的前端请求应该都需要接收到后端返回的未授权状态

  4. 如果 允许放行

    1. 会根据请求调用对应的接口加载资源

      (比如生成接口文档无需验证都可放行http://localhost:8080/doc.html)

    2. 登录请求也不需要验证即可放行,我们需要在表单中拿到用户的身份信息进行进一步的身份认证

      1. Authentication Manager认证管理器会进行用户信息认证

      2. Authentication Provider 会调用UserDetailsService,将用户名与数据库中的用户信息进行匹配,同时验证密码

      3. 如果验证成功,创建一个Authentication认证对象,包含用户及权限信息

    3. 验证成功后,Spring Security会调用配置的AuthenticationSuccessHandler,将用户信息返回给前端

      我们存在localStorage中的userVO就是通过上面的AuthenticationSuccessHandler返回给前端的

进阶练习,在你的项目中使用Security

新增依赖(在你的pom.xml中添加):

 <!-- Security依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!-- JSON处理工具类 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.36</version>
        </dependency>

自定义配置类SpringSecurityConfig

  1. 我们需要在cn.tedu.ivos.base 包下新建一个security 包

  2. 在 security 包下创建一个自定义配置类 SpringSecurityConfig

  1. 这个配置类需要继承WebSecurityConfigurerAdapter
  • SpringSecurity框架提供了WebSecurityConfigurerAdapter安全基类

  • 该类提供了默认的安全配置,当我们的自定义类继承该类时,实际上在创建一个配置Bean

  • 这个Bean会被Spring自动检测到并用来定制我们自己的安全设置

  • 通过继承可以大大简化安全配置,而且重写 configure(HttpSecurity http)方法可以定制化内容

package cn.tedu.ivos.base.security;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

//表示这是一个配置类,交由Spring容器管理
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

}

设置请求放行的处理方案(登录后端改造)

SpringSecurity基础安全配置1

重写configure(HttpSecurity http)进行配置

这些配置组合起来,创建了一个基础的安全策略,其中大部分资源需要认证,登录页面对所有人开放,且使用HTTP基本认证和自定义的登录成功/失败处理器

package cn.tedu.ivos.base.security;

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

//表示这是一个配置类,交由Spring容器管理
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //super.configure(http);//这个表示调用父类的实现,我们不需要
        http.csrf().disable() // 禁用CSRF(跨站请求伪造)保护
                .httpBasic() // 配置HTTP基本认证
                .and().authorizeRequests() // 配置请求授权,默认禁用所有请求
                .antMatchers().permitAll() // 对指定的资源不进行权限检查,允许所有用户访问
                .anyRequest().authenticated() //对其他所有请求要求用户必须认证通过
                .and().formLogin() // 配置表单登录,默认拦截的登录请求地址为/login
                .successHandler(authenticationSuccessHandler) //设置认证成功处理器
                .failureHandler(authenticationFailureHandler) //设置认证失败处理器
                .permitAll() //允许所有用户进行登录尝试
        ;
    }
}
自定义的认证成功处理器CustomAuthenticationSuccessHandler

当用户成功通过认证后,会调用这个处理器的onAuthenticationSuccess方法

package cn.tedu.ivos.base.security;

import cn.tedu.ivos.base.response.JsonResult;
import cn.tedu.ivos.user.mapper.UserMapper;
import cn.tedu.ivos.user.pojo.vo.UserVO;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Service;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*
 * 自定义的认证成功处理器
 * 当用户成功通过认证后,会调用这个处理器的onAuthenticationSuccess方法
 * */
@Slf4j
@Service
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Autowired
    UserMapper userMapper;
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        //可以根据我们的业务需要编写认证成功后的逻辑
        log.debug("认证成功");
        //从认证信息中获取用户名信息
        String username = authentication.getName();
        //根据用户名查询用户信息
        UserVO userVO = userMapper.selectByUsername(username);
        //设置响应的字符编码和内容类型,确保前端能正确解析JSON格式的数据
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json;charset=UTF-8");
        //将查询到的用户信息封装成JSON格式,写入响应中返回给前端
        response.getWriter().write(JSON.toJSONString(JsonResult.ok(userVO)));
    }
}
自定义的认证失败处理器CustomAuthenticationFailureHandler

当用户认证失败后,会调用这个处理器的onAuthenticationFailure方法

package cn.tedu.ivos.base.security;

import cn.tedu.ivos.base.response.JsonResult;
import cn.tedu.ivos.base.response.StatusCode;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Service;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/*
 * 自定义的认证失败处理器
 * 当用户认证失败后,会调用这个处理器的onAuthenticationFailure方法
 * */
@Slf4j
@Service
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        //可以根据我们的业务需要编写认证失败后的逻辑
        log.debug("认证失败");
        //设置响应的字符编码为UTF-8,确保传输的数据在客户端能够正确解析
        response.setCharacterEncoding("UTF-8");
        //设置响应的内容类型为application/json,指定客户端应以JSON格式解析响应数据
        //同时指定字符集为UTF-8,与前面的设置保持一致
        response.setContentType("application/json;charset=UTF-8");
        //将用户名错误的响应结果转换为JSON格式,并写入响应流
        //JsonResult封装了状态码和可能的错误信息,这里使用StatusCode.USERNAME_ERROR表示用户名错误的特定状态码
        response.getWriter().write(JSON.toJSONString(new JsonResult(StatusCode.USERNAME_ERROR)));
    }
}
SpringSecurity基础安全配置2

重写configure(AuthenticationManagerBuilder auth) 方法

package cn.tedu.ivos.base.security;

import org.springframework.beans.factory.annotation.Autowired;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

//表示这是一个配置类,交由Spring容器管理
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    CustomAuthenticationSuccessHandler authenticationSuccessHandler;
    @Autowired
    CustomAuthenticationFailureHandler authenticationFailureHandler;
    @Autowired
    CustomAuthenticationProvider authenticationProvider;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //super.configure(http);//这个表示调用父类的实现,我们不需要
        http.csrf().disable() // 禁用CSRF(跨站请求伪造)保护
                .httpBasic() // 配置HTTP基本认证
                .and().authorizeRequests() // 配置请求授权,默认禁用所有请求
                .antMatchers().permitAll() // 对指定的资源不进行权限检查,允许所有用户访问
                .anyRequest().authenticated() //对其他所有请求要求用户必须认证通过
                .and().formLogin() // 配置表单登录,默认拦截的登录请求地址为/login
                .successHandler(authenticationSuccessHandler) //设置认证成功处理器
                .failureHandler(authenticationFailureHandler) //设置认证失败处理器
                .permitAll() //允许所有用户进行登录尝试
        ;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //super.configure(auth);
        auth.authenticationProvider(authenticationProvider);
    }
}

后续稍后更新....

相关推荐
守护者17019 分钟前
JAVA学习-练习试用Java实现“使用Arrays.toString方法将数组转换为字符串并打印出来”
java·学习
源码哥_博纳软云20 分钟前
JAVA同城服务场馆门店预约系统支持H5小程序APP源码
java·开发语言·微信小程序·小程序·微信公众平台
禾高网络22 分钟前
租赁小程序成品|租赁系统搭建核心功能
java·人工智能·小程序
学会沉淀。28 分钟前
Docker学习
java·开发语言·学习
如若12329 分钟前
对文件内的文件名生成目录,方便查阅
java·前端·python
初晴~1 小时前
【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)
java·数据库·redis·分布式·后端·spring·
黑胡子大叔的小屋2 小时前
基于springboot的海洋知识服务平台的设计与实现
java·spring boot·毕业设计
ThisIsClark2 小时前
【后端面试总结】深入解析进程和线程的区别
java·jvm·面试
雷神乐乐3 小时前
Spring学习(一)——Sping-XML
java·学习·spring
小林coding3 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云