找回密码
 立即注册
首页 业界区 业界 Spring Security认证与授权

Spring Security认证与授权

百谖夷 2025-6-2 21:23:43
什么是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来进行拦截
1.png


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


  • 添加Spring Security相关依赖
    1.                                 <dependency>
    2.         <groupId>org.springframework.boot</groupId>
    3.         spring-boot-starter-security</artifactId>
    4.     </dependency>
    5.     <dependency>
    6.         <groupId>io.jsonwebtoken</groupId>
    7.         jjwt-api</artifactId>
    8.         <version>0.12.6</version>
    9.     </dependency>
    10.     <dependency>
    11.         <groupId>io.jsonwebtoken</groupId>
    12.         jjwt-impl</artifactId>
    13.         <version>0.12.6</version>
    14.         <scope>runtime</scope>
    15.     </dependency>
    16.     <dependency>
    17.         <groupId>io.jsonwebtoken</groupId>
    18.         jjwt-jackson</artifactId>
    19.         <version>0.12.6</version>
    20.         <scope>runtime</scope>
    21.     </dependency>
    22.    
    23.     <dependency>
    24. <groupId>com.auth0</groupId>
    25. java-jwt</artifactId>
    26. <version>3.10.3</version>
    27.                         </dependency>
    28.                        
    29.    
    30.     <dependency>
    31.         <groupId>com.alibaba.fastjson2</groupId>
    32.         fastjson2</artifactId>
    33.         <version>2.0.1</version>
    34.     </dependency>
    复制代码
编写UserController进行测试
2.png

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


  • 默认的用户名:user
  • 其密码会在项目启动时打印在控制台
4.png


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

用户认证

用户认证的流程
6.png

核心接口:

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

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

  • Spring Security三个认证核心组件为:

    • Authentication:存储认证信息的上下文,代表当前用户
    • SecurityContext:上下文对象,用来获取Authentication
    • SecurityContextHolder:上下文管理对象,用来获取SecurityContext

认证逻辑


  • AuthenticationManager是Spring Security用于执行身份验证的组件,其authenticate方法可以完成认证.Spring Security默认的认证方法是在UsernamePasswordAuthenticationFilter这个过滤器中进行认证的
    关键代码如下:
    1.         // 创建用户认证令牌,使用用户名和密码作为凭证
    2.         UsernamePasswordAuthenticationToken token =
    3.                 new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
    4.         // 通过认证管理器执行Spring Security认证流程,返回包含用户详情的认证对象
    5.         Authentication authenticate = authenticationManager.authenticate(token);
    复制代码
加密器PasswordEncoder


  • passwordEncoder在Spring Security中用于处理密码加密存储和验证.
  • 它可以负责将用户提交的明文密码转换为不可逆的加密字符串(如BCrypt算法),之后便将密码存储到数据库中
  • 在用户登录时,验证用户输入的明文密码是否与存储的加密密码一致
若需要自定义加密方法,我们可以编写自定义加密器CustomPasswordEncoder
  1. public class CustomPasswordEncoder implements PasswordEncoder {
  2.     // 自定义密码加密方式,使用MD5加密算法
  3.     @Override
  4.     public boolean matches(CharSequence rawPassword, String encodedPassword) {
  5.         return Arrays.toString(DigestUtils.md5Digest(rawPassword.toString().getBytes())).equals(encodedPassword);
  6.     }
  7.     // 自定义密码加密方式,使用MD5加密算法
  8.     @Override
  9.     public String encode(CharSequence rawPassword) {
  10.         return Arrays.toString(DigestUtils.md5Digest(rawPassword.toString().getBytes()));
  11.     }
  12. }
复制代码
并在SecurityConfig中注册新的加密器
  1. @Configuration
  2. @EnableWebSecurity
  3. public class SecurityConfig extends WebSecurityConfiguration {
  4.     // 密码加密器
  5.     @Bean
  6.     public PasswordEncoder passwordEncoder() {
  7.         return new CustomPasswordEncoder();
  8.     }
  9. }
复制代码
直接使用BCryptPasswordEncoder加密器
  1. @Configuration
  2. @EnableWebSecurity
  3. public class SecurityConfig extends WebSecurityConfiguration {
  4.     // 密码加密器
  5.     @Bean
  6.     public PasswordEncoder passwordEncoder() {
  7.         return new BCryptPasswordEncoder();
  8.     }
  9. }
复制代码
自定义登录接口


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


  • 接着需要把AuthenticationManager注入容器→因为要使用其的authenticate方法进行验证
  1. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  2.     /**
  3.      * 配置密码编码器
  4.      *
  5.      * @return
  6.      */
  7.     @Bean
  8.     public PasswordEncoder passwordEncoder() {
  9.         return new CustomPasswordEncoder();
  10.     }
  11.     /**
  12.      * 配置HTTP安全设置
  13.      *
  14.      * @param http
  15.      * @throws Exception
  16.      */
  17.     @Override
  18.     protected void configure(HttpSecurity http) throws Exception {
  19.         http
  20.                 // 禁用CSRF保护(常用于API场景)
  21.                 .csrf().disable()
  22.                 // 配置会话管理为无状态(不创建和使用HTTP Session)
  23.                 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
  24.                 .and()
  25.                 // 配置请求授权规则
  26.                 .authorizeRequests()
  27.                 // 允许匿名访问登录端点
  28.                 .antMatchers("/user/login").permitAll()
  29.                 // 所有其他请求需要认证
  30.                 .anyRequest().authenticated();
  31.     }
  32.     /**
  33.      * 暴露AuthenticationManager
  34.      *
  35.      * @return
  36.      * @throws Exception
  37.      */
  38.     @Bean
  39.     @Override
  40.     protected AuthenticationManager authenticationManager() throws Exception {
  41.         return super.authenticationManager();
  42.     }
  43.    
  44. }
复制代码
<ul>IUserService

  • 在其中编写login()方法来实现登录逻辑
  1. public interface ISysUserService extends IService<SysUser> {
  2.     /**
  3.      * @description: 登录
  4.      * @author: HYJ
  5.      * @date: 2025/4/15 0:01
  6.      * @param: [user]
  7.      * @return: edu.ptu.springsecurity.common.AjaxResult
  8.      **/
  9.     AjaxResult login(SysUser user);
  10. }
复制代码
LoginUser

  • LoginUser实现UserDetails接口,重写其中的方法.将业务数据衔接到Spring Security的认证体系中
[code]// 忽略未知的属性,避免序列化时出现异常@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

相关推荐

您需要登录后才可以回帖 登录 | 立即注册