【SpringSecurity】SpringSecurity安全框架登录校验流程与登录配置示例

SpringSecurity安全框架

Security 是一个能够为基于 Spring 的应用程序提供认证、授权以及保护免受攻击的安全框架。它是 Spring 生态系统的一部分,与 Spring 框架无缝集成。这些框架帮助开发者实现认证(Authentication)、授权(Authorization)、加密(Encryption)和其他安全相关功能,以保护数据的机密性、完整性和可用性。

  • 认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户
  • 授权:经过认证后判断当前用户是否有权限进行某个操作

Apache Shiro 是另一个针对 Java 应用的安全框架,提供了简单易用的API来处理认证、授权、加密和会话管理。相较于 SpringSecurity,Shiro 更加轻量级,适用于小型到中型应用程序,但提供的安全特性没有那么深入或复杂。适用于不需要与 Spring 框架集成的应用程序,或者对安全框架的轻量级有要求的场景。(不推荐使用)

在 SpringBoot 项目中引入 SpringSecurity 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

随意编写一个 controller 请求

@RestController
@RequestMapping("/index")
public class IndexController {
    @RequestMapping("/info")
    public String info(){
        return "index";
    }
}

当尝试访问接口就会自动跳转到一个 SpringSecurity 的默认登陆页面,默认用户名是 user,密码会输出在控制台。必须登陆之后才能对接口进行访问。若用户名或密码错误则无法成功访问。

在这里插入图片描述

登录校验流程

在这里插入图片描述

1、前端发起请求调用登录接口,携带用户名、密码

2、和数据库用户名、密码进行校验

3、校验成功则使用用户 ID 生成唯一令牌 token(JWT加密),存入 redis

4、将 token 响应给前端,由前端存储

5、登录后访问其他请求,在请求头中携带 token

6、解析 token(JWT解密),获取用户 ID,根据 ID 获得 redis 中的存储对象,查询权限是否可以访问

7、响应前端数据

SpringSecurity 的原理其实就是一个过滤器链,内部包含了提供各种功能的过滤器。

在这里插入图片描述

图中只展示了核心过滤器,其它的非核心过滤器并没有在图中展示。

UsernamePasswordAuthenticationFilter:负责处理基于表单的登录请求。当用户提交了用户名和密码后,会捕获这个请求,并尝试进行认证。

ExceptionTranslationFilter:处理在认证或授权过程中抛出的异常。

FilterSecurityInterceptor:是过滤器链中负责实际执行授权决策的过滤器。它会检查当前用户是否有权限访问所请求的资源。

可以通过 Debug 查看当前系统中 SpringSecurity 过滤器链中有哪些过滤器及它们的顺序。

在这里插入图片描述

登录配置示例

随机生成的密码每次启动时都会变。若对登录的用户名/密码进行配置,有三种不同的方式:

  1. 在 application.yml 中进行配置(自动加密)
spring:
  security:
    user:
      name: user
      password: 123
  1. 通过 Java 代码配置在内存中
/**
 * @Description: security 配置类
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 初始化账号密码
        auth.inMemoryAuthentication()
                .passwordEncoder(new BCryptPasswordEncoder())
                .withUser("user")
                .password(passwordEncoder().encode("123")) // 密码加密
                .roles("USER"); // 权限配置
    }
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
  1. 通过 Java 从数据库中加载

创建数据库表以及实体类;数据库连接、mybatis-plus 的依赖引入;yml 相关配置;dao 层构建

创建 LoginAuthenticationProvider 类,实现接口 AuthenticationProvider,完成登录认证功能

@Component
public class LoginAuthenticationProvider implements AuthenticationProvider {
    @Resource
    private UserDao userDao;
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 认证逻辑
        // 获取页面中的账号密码
        String userName = (String) authentication.getPrincipal();
        String password = (String) authentication.getCredentials();
        // 调用数据库进行校验
        User user = userDao.selectOne(new QueryWrapper<User>()
                .eq("userCode",userName)
                .eq("userPassword",password)
        );
        // 认证登录状态
        if (user != null) {
            return new UsernamePasswordAuthenticationToken(user
                    , password
                    , AuthorityUtils
                        .commaSeparatedStringToAuthorityList("ROLE_USER"));
        }
        return null;
    }

    /**
     * 判断是否支持 如果该 AuthenticationProvider 支持传入的 Authentication 对象,则返回 true
     * @param authentication 认证
     * @return
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}

修改 security 配置类 SecurityConfig

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // 实例化过滤器
    @Resource
    private LoginAuthenticationProvider loginAuthenticationProvider;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 替换认证的过滤器
        auth.authenticationProvider(loginAuthenticationProvider);
    }
    @Override
    public void configure(HttpSecurity http) throws Exception {
        // 关闭csrf防护
        http.csrf().disable()
                .headers().frameOptions().disable()
                .and();
        // 登录处理
        http.formLogin() //表单方式,或httpBasic
                .loginPage("/login.html") //自定义登录页面
                .loginProcessingUrl("/form") //表单提交路径
                .defaultSuccessUrl("/index/info") //成功登陆后跳转页面
                .usernameParameter("userCode")
                .passwordParameter("password")
                .permitAll(); //允许所有用户访问
        // 权限配置 授权
        http.authorizeRequests()
                .antMatchers("/").permitAll() //允许所有用户访问
                .antMatchers("/index/**").hasAnyRole("ADMIN", "USER") //index下的页面ADMIN USER两角色都可以访问
                .antMatchers("/user/**").hasRole("USER")
                .antMatchers("/admin/**").hasRole("ADMIN");
    }
}

编写页面和 Controller 进行测试

CSRF

跨站请求伪造(Cross-site request forgery)是一种对网站的恶意利用,也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。 CSRF 跨站点请求伪造跟 XSS 跨网站脚本攻击一样存在巨大的危害性。

跟 XSS 相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

在这里插入图片描述

CSRF攻击攻击原理及过程如下

  1. 用户打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
  2. 在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
  3. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
  4. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
  5. 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。

所以要被CSRF攻击,必须同时满足两个条件:

  1. 登录受信任网站A,并在本地生成Cookie。
  2. 在不登出A的情况下,访问危险网站B。

SpringSecurity 防止 CSRF 攻击的方式是通过 csrf_token。后端会生成一个 csrf_token,前端发起请求的时候需要携带这个 csrf_token,后端会有过滤器进行校验,如果没有携带或者是伪造的就不允许访问。

CSRF 攻击依靠的是 cookie 中所携带的认证信息。但是在前后端分离的项目中我们的认证信息其实是 token,而 token 并不是存储中 cookie 中,并且需要前端代码去把 token 设置到请求头中才可以,所以 CSRF 攻击也就不用担心了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值