九SpringSecurity Web权限方案—— CSRF

Posted 上善若水

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了九SpringSecurity Web权限方案—— CSRF相关的知识,希望对你有一定的参考价值。

一、CSRF

1.1、理解

跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack或者session riding,通常缩写为CSRF或者XSRF,是一种挟制用户在当前已登录的web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS利用的是用户对指定网站的信任,CSRF利用的是网站对用户网页浏览器的信任。
跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件、发消息,甚至财产操作,如转账和购买商品)。由于浏览器曾经认证过,所以被访问得网站会认为是真正的用户操作而去运行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的
从Spring Security 4.0 开始,默认情况下会启用 CSRF 保护,以防止 CSRF 攻击应用程序,Spring Security CSRF 会针对 PATCH,POST,PUT 和 DELETE 方法进行防护。

1.2、案例

  • 项目结构:
  • 配置文件

关闭安全配置的类中的 csrf

package com.xbmu.csrfsecurity.config;

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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import javax.annotation.Resource;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter 
    @Resource
    UserDetailsService userDetailsService;

    //实现用户身份认证
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        //配置url的访问权限
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/**update**").permitAll()
                .antMatchers("/login/**").permitAll()
                .anyRequest().authenticated();

        //关闭csrf保护功能
        //http.csrf().disable();

        //使用自定义的登录窗口
        http.formLogin()
                .loginPage("/userLogin").permitAll()
                .usernameParameter("username").passwordParameter("password")
                .defaultSuccessUrl("/")
                .failureUrl("/userLogin?error");
    

  • 控制层代码
package com.xbmu.csrfsecurity.controller;


import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class CSRFController 

     @GetMapping("/toupdate")
     public String test(Model model)
         return "csrf/csrfTest";
     

    @PostMapping("/update_token")
    public String getToken() 
        return "csrf/csrf_token";
    

  • 服务层代码
package com.xbmu.csrfsecurity.service;

import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class UserDetailsServiceImpl implements UserDetailsService 

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException 

            List<SimpleGrantedAuthority> list = new ArrayList<>();
            list.add(new SimpleGrantedAuthority("role"));
            UserDetails userDetails = new User("lucy", new BCryptPasswordEncoder().encode("123")
                    , list);
            return userDetails;
    

  • 在登录页面添加一个隐藏域
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>用户修改</title>
</head>
<body>
<div align="center">
    <form  method="post" action="update_token">
      <input type="hidden" th:name="$_csrf.parameterName" th:value="$_csrf.token"/>
        用户名: <input type="text" name="username" /><br />&nbsp;&nbsp;码: <input type="password" name="password" /><br />
        <button type="submit">登录</button>
    </form>
</div>
</body>
</html>
  • 测试

1.3、原理

  1. 生成 csrfToken 保存到 HttpSession 或者 Cookie 中。


    SaveOnAccessCsrfToken 类有个接口 CsrfTokenRepository

    当前接口实现类:HttpSessionCsrfTokenRepositoryCookieCsrfTokenRepository
  2. 请求到来时,从请求中提取 csrfToken,和保存的 csrfToken 做比较,进而判断当前请求是否合法。主要通过 CsrfFilter 过滤器来完成。

以上是关于九SpringSecurity Web权限方案—— CSRF的主要内容,如果未能解决你的问题,请参考以下文章

[SpringSecurity]web权限方案_用户注销

六SpringSecurity Web权限方案—— 基于数据库实现权限认证

六SpringSecurity Web权限方案—— 基于数据库实现权限认证

[SpringSecurity]web权限方案_用户授权_自定义403页面

五SpringSecurity Web权限方案——自定义登录页面与权限访问控制

五SpringSecurity Web权限方案——自定义登录页面与权限访问控制