百谖夷 发表于 2025-6-2 21:23:43

Spring Security认证与授权

什么是Spring Security

Spring Security是基于Spring框架,提供了一套Web应用安全性框架.专门为Java应用提供用户认证(Authentication)和用户授权(Authorization),支持单体应用到微服务的全场景安全防护
认证(Authentication)


[*]验证某个用户是否为系统中的合法主体,即确认该用户是否可以访问此系统.认证一般需要用户提供用户名与密码,提供校验用户名与密码完成认证过程
[*]简单来说→认证是判断该用户是否能登录;
[*]关键要素:1.Principal:用户主体(如用户名)2.Credentials:验证凭证(如用户密码)3.Authorities:用户权限集合
授权(Authorization)


[*]是指某个用户是否有权限执行某个操作.在同一系统中,不同用户所具有的权限是不同的.如对某一文件,有些用户只能读不能修改,而有些用户既可读也可以修改.某个角色都有一系列的权限
[*]简单来说→授权是判断该用户是否有权限去做特定的操作;
[*]关键要素:1.角色(Role):用户分组标识2.权限(Permission):具体操作权限
优势和缺点


[*]优势

[*]深度与Spring整合:无缝支持Spring Boot、Spring MVC、Spring Data等框架
[*]企业级安全方案:支持OAuth2,SAML,LDAP,JWT等协议,满足复杂安全需求
[*]旧版本无法脱离Web环境
[*]新版本对框架进行分层提取,分为核心板块和Web板块,

[*]缺点

[*]性能开销:默认开启CSRF,Session管理等特性,对高性能场景需手动优化
[*]配置复杂度高:默认配置覆盖大量安全规则,需要显式覆盖才能简化

与Shiro对比

对比维度Spring SecurityShiro生态整合深度集成 Spring 技术栈需手动整合Spring,对非 Spring 项目更友好微服务支持天然支持 Spring Cloud Security需自行实现分布式会话和权限管理典型场景企业级应用,微服务架构,需要 OAuth2 的 SaaS 系统中小型 Web 应用,移动端后台,快速开发项目

[*]一般来说常见的安全管理技术栈组合是这样:

[*]SSM+Shiro
[*]Spring Boot/Spring Cloud+Spring Security

Spring Security实现原理

对Web资源最好的保护是Filter,对方法调用的最好方法是AOP


[*]Spring Security进行认证和权限检验时就是通过一系列的Filter来进行拦截


[*]如图所示:一个请求要访问到后端,就要从左到右经过这些过滤器.其中绿色的过滤器是负责认证的过滤器,蓝色部分是负责异常处理的过滤器,橙色是负责权限校验的拦截器.
[*]对于我们而言,只需关注UserNamePasswordAuthenticationFilter**->负责登入认证和FilterSecurityInterceptor->负责授权**
对于Spring Security,掌握了过滤器和组件就完全掌握了Spring Security.其使用方法就是对过滤器和组件进行扩展
Spring Security入门


[*]添加Spring Security相关依赖
                                <dependency>
      <groupId>org.springframework.boot</groupId>
      spring-boot-starter-security</artifactId>
    </dependency>

    <dependency>
      <groupId>io.jsonwebtoken</groupId>
      jjwt-api</artifactId>
      <version>0.12.6</version>
    </dependency>

    <dependency>
      <groupId>io.jsonwebtoken</groupId>
      jjwt-impl</artifactId>
      <version>0.12.6</version>
      <scope>runtime</scope>
    </dependency>

    <dependency>
      <groupId>io.jsonwebtoken</groupId>
      jjwt-jackson</artifactId>
      <version>0.12.6</version>
      <scope>runtime</scope>
    </dependency>
   
    <dependency>
<groupId>com.auth0</groupId>
java-jwt</artifactId>
<version>3.10.3</version>
                        </dependency>
                       
   
    <dependency>
      <groupId>com.alibaba.fastjson2</groupId>
      fastjson2</artifactId>
      <version>2.0.1</version>
    </dependency>
编写UserController进行测试

启动项目,访问localhost:8080进行测试,其会自动跳转到localhost:8080/login登入页面


[*]默认的用户名:user
[*]其密码会在项目启动时打印在控制台


[*]输入正确的用户名和密码时,即可成功访问UserController中的get方法→说明Spring Security保护生效
[*]当然在实际开发中,这种默认配置是不存在的,我们需要扩展这些组件

用户认证

用户认证的流程

核心接口:

[*]Authentication接口,表示当前访问系统的用户,封装了用户的信息,其实现类为UsernamePasswordAuthenticationToken
[*]AuthenticationManager接口,其定义了Authentication的方法
[*]UserDetailsService接口,加载用户特定数据的核心接口,其中定义了一个根据用户名查询用户信息的方法
[*]UserDetails接口,提供核心用户信息.将UserDetailsService中获取的信息封装为UserDetails对象返回.并将其封装至Authentication对象中
UserDetails中的基本方法:

用于认证的核心组件:
对于系统来说,同一时间会有多个用户正在使用,那么如何确认哪个用户正在请求登录接口是登录认证的核心目的.Spring Security提出了:当前登录用户/当前认证用户,Spring Security中使用Authentication来存储认证信息,表示当前用户
在Spring boot中使用安全上下文SecurityContext来获取Authentication,SecurityContext交有SecurityContextHolder来管理,使用以下方法即可获取Authentication
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

[*]Spring Security三个认证核心组件为:
[*]Authentication:存储认证信息的上下文,代表当前用户
[*]SecurityContext:上下文对象,用来获取Authentication
[*]SecurityContextHolder:上下文管理对象,用来获取SecurityContext

认证逻辑


[*]AuthenticationManager是Spring Security用于执行身份验证的组件,其authenticate方法可以完成认证.Spring Security默认的认证方法是在UsernamePasswordAuthenticationFilter这个过滤器中进行认证的
关键代码如下:
      // 创建用户认证令牌,使用用户名和密码作为凭证
      UsernamePasswordAuthenticationToken token =
                new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());

      // 通过认证管理器执行Spring Security认证流程,返回包含用户详情的认证对象
      Authentication authenticate = authenticationManager.authenticate(token);
加密器PasswordEncoder


[*]passwordEncoder在Spring Security中用于处理密码加密存储和验证.
[*]它可以负责将用户提交的明文密码转换为不可逆的加密字符串(如BCrypt算法),之后便将密码存储到数据库中
[*]在用户登录时,验证用户输入的明文密码是否与存储的加密密码一致
若需要自定义加密方法,我们可以编写自定义加密器CustomPasswordEncoder
public class CustomPasswordEncoder implements PasswordEncoder {
    // 自定义密码加密方式,使用MD5加密算法
    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
      return Arrays.toString(DigestUtils.md5Digest(rawPassword.toString().getBytes())).equals(encodedPassword);
    }

    // 自定义密码加密方式,使用MD5加密算法
    @Override
    public String encode(CharSequence rawPassword) {
      return Arrays.toString(DigestUtils.md5Digest(rawPassword.toString().getBytes()));
    }
}并在SecurityConfig中注册新的加密器
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfiguration {

    // 密码加密器
    @Bean
    public PasswordEncoder passwordEncoder() {
      return new CustomPasswordEncoder();
    }
}直接使用BCryptPasswordEncoder加密器
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfiguration {

    // 密码加密器
    @Bean
    public PasswordEncoder passwordEncoder() {
      return new BCryptPasswordEncoder();
    }
}自定义登录接口


[*]首先要重写SecuritySpring中的方法,可以自己写使用@Bean注册,也可以重写WebSecurityConfigurerAdapter接口的方法
由于Spring Security会对每一个接口都会进行认证,有些接口需要放行,直接让用户访问,我们就得在config()中进行放行


[*]接着需要把AuthenticationManager注入容器→因为要使用其的authenticate方法进行验证
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
   * 配置密码编码器
   *
   * @return
   */
    @Bean
    public PasswordEncoder passwordEncoder() {
      return new CustomPasswordEncoder();
    }

    /**
   * 配置HTTP安全设置
   *
   * @param http
   * @throws Exception
   */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
      http
                // 禁用CSRF保护(常用于API场景)
                .csrf().disable()
                // 配置会话管理为无状态(不创建和使用HTTP Session)
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                // 配置请求授权规则
                .authorizeRequests()
                // 允许匿名访问登录端点
                .antMatchers("/user/login").permitAll()
                // 所有其他请求需要认证
                .anyRequest().authenticated();
    }

    /**
   * 暴露AuthenticationManager
   *
   * @return
   * @throws Exception
   */
    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
      return super.authenticationManager();
    }
   
}<ul>IUserService

[*]在其中编写login()方法来实现登录逻辑
public interface ISysUserService extends IService<SysUser> {

    /**
   * @description: 登录
   * @author: HYJ
   * @date: 2025/4/15 0:01
   * @param:
   * @return: edu.ptu.springsecurity.common.AjaxResult
   **/
    AjaxResult login(SysUser user);
}LoginUser

[*]LoginUser实现UserDetails接口,重写其中的方法.将业务数据衔接到Spring Security的认证体系中
// 忽略未知的属性,避免序列化时出现异常@JsonIgnoreProperties(ignoreUnknown = true)public class LoginUser implements UserDetails {    // 用户信息    private SysUser user;    /**   * @description: 账号是否未过期   * @author: HYJ   * @date: 2025/4/15 0:02   * @param: []   * @return: boolean   **/    @Override    public boolean isEnabled() {      return true;    }    /**   * @description: 密码是否未过期   * @author: HYJ   * @date: 2025/4/15 0:02   * @param: []   * @return: boolean   **/    @Override    public boolean isCredentialsNonExpired() {      return true;    }    /**   * @description: 账号是否未锁定   * @author: HYJ   * @date: 2025/4/15 0:02   * @param: []   * @return: boolean   **/    @Override    public boolean isAccountNonLocked() {      return true;    }    /**   * @description: 账号是否未过期   * @author: HYJ   * @date: 2025/4/15 0:02   * @param: []   * @return: boolean   **/    @Override    public boolean isAccountNonExpired() {      return true;    }    /**   * @description: 获取用户名   * @author: HYJ   * @date: 2025/4/15 0:02   * @param: []   * @return: java.lang.String   **/    @Override    public String getUsername() {      return user.getUsername();    }    /**   * @description: 获取密码   * @author: HYJ   * @date: 2025/4/15 0:02   * @param: []   * @return: java.lang.String   **/    @Override    public String getPassword() {      return user.getPassword();    }    /**   * @description: 获取权限   * @author: HYJ   * @date: 2025/4/15 0:02   * @param: []   * @return: java.util.Collection
页: [1]
查看完整版本: Spring Security认证与授权