SpringSercurity笔记

SpringSercurity

1. 概述

1. 简介

Spring Security 是一个专注于为 Java 应用程序提供身份验证和授权的框架。与所有 Spring 项目一样,Spring Security 的真正强大之处在于它可以轻松扩展以满足自定义要求。

特点:

  1. 对身份验证和授权的全面且可扩展的支持
  2. 防止会话固定、点击劫持、跨站点请求伪造等攻击
  3. Servlet API 集成
  4. 与 Spring Web MVC 的可选集成

2. 安全方面两大核心

用户认证(Authentication)

验证某个用户是否为系统中的合法主体

授权(Authorization)

用户是否有权限去做某些事情

3. 同款产品对比

SpringSercurity

特点:

  1. 和Spring无缝整合。
  2. 全面的权限控制。 专门为Web开发而设计。
  3. 旧版本不能脱离Web环境使用。
  4. 新版本对整个框架进行了分层抽取,分成了核心模块和Web模块。单独引入核心模块就可以脱离Web环境。
  5. 重量级。
Shiro

Apache旗下的轻量级权限控制框架。

特点:

  1. 轻量级。Shiro主张的理念是把复杂的事情变简单。针对对性能有更高要求的互联网应用有更好表现。
  2. 通用性。
  3. 好处:不局限于Web环境,可以脱离Web环境使用。
  4. 缺陷:在Web环境下一些特定的需求需要手动编写代码定制。
推荐组合:

SSM + Shiro

Springboot/SpringCloud + SpringSercurity

4. 模块

待补充……

2. 入门案例

1. 映入依赖
<dependencies>

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

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

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
2. 编写controller
package com.zhan.demo01.controller;


import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello(){
        return "hello Spring Security";
    }
}
3. 启动测试

image-20210820235538725

image-20210820235609168

# 输入用户名 user 
# 输入随机密码  08b46078-0cd1-42b6-b26b-780a3c8a89d5
# 点击登录

image-20210820235835397

这里说明了一个问题;那就是访问controller的请求被springsecurity拦截了!接下来我们来看看源码,springsecuriity如何拦截了我们的请求:

3. Springsecurity基本原理

本质:Springsecurity本质上是一个过滤器链,有很多过滤器链

常见过滤器器
  1. org.springframework.security.web.context.SecurityContextPersistenceFilter
    首当其冲的一个过滤器,作用之重要,自不必多言。
    SecurityContextPersistenceFilter主要是使用SecurityContextRepository在session中保存或更新一个
    SecurityContext,并将SecurityContext给以后的过滤器使用,来为后续filter建立所需的上下文。
    SecurityContext中存储了当前用户的认证以及权限信息
  2. org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
    此过滤器用于集成SecurityContext到Spring异步执行机制中的WebAsyncManager
  3. org.springframework.security.web.header.HeaderWriterFilter
    向请求的Header中添加相应的信息,可在http标签内部使用security:headers来控制
  4. org.springframework.security.web.csrf.CsrfFilter
    csrf又称跨域请求伪造,SpringSecurity会对所有post请求验证是否包含系统生成的csrf的token信息,
    如果不包含,则报错。起到防止csrf攻击的效果。
  5. org.springframework.security.web.authentication.logout.LogoutFilter
    匹配 URL为/logout的请求,实现用户退出,清除认证信息。
  6. org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
    认证操作全靠这个过滤器,默认匹配URL为/login且必须为POST请求。
  7. org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
    如果没有在配置文件中指定认证页面,则由该过滤器生成一个默认认证页面。
  8. org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
    由此过滤器可以生产一个默认的退出登录页面
  9. org.springframework.security.web.authentication.www.BasicAuthenticationFilter
    此过滤器会自动解析HTTP请求中头部名字为Authentication,且以Basic开头的头信息。
  10. org.springframework.security.web.savedrequest.RequestCacheAwareFilter
    通过HttpSessionRequestCache内部维护了一个RequestCache,用于缓存HttpServletRequest
  11. org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
    针对ServletRequest进行了一次包装,使得request具有更加丰富的API
  12. org.springframework.security.web.authentication.AnonymousAuthenticationFilter
    当SecurityContextHolder中认证信息为空,则会创建一个匿名用户存入到SecurityContextHolder中。
    spring security为了兼容未登录的访问,也走了一套认证流程,只不过是一个匿名的身份。
  13. org.springframework.security.web.session.SessionManagementFilter
    SecurityContextRepository限制同一用户开启多个会话的数量
  14. org.springframework.security.web.access.ExceptionTranslationFilter
    异常转换过滤器位于整个springSecurityFilterChain的后方,用来转换整个链路中出现的异常
  15. org.springframework.security.web.access.intercept.FilterSecurityInterceptor
    获取所配置资源访问的授权信息,根据SecurityContextHolder中存储的用户信息来决定其是否有权限。

4. 用户认证

1. 用户密码配置

1.1 配置文件配置
# springsecurity配置
spring:
  security:
    user:
      name: salvation
      password: 12345
1.2 配置类配置
package com.zhan.demo01.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        super.configure(auth);
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("12345");//密码加密
        auth.inMemoryAuthentication().withUser("salvation").password(password).roles("admin");

    }

    /**
     * 注入PasswordEncoder接口实现类,因为springSecurity初始化是需要PasswordEncoder接口实现类来解析密码
     * @return {@link PasswordEncoder}
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

缺点:用户名密码都写死了,现实中需要从数据库中获取用户密码权限

1.3 自定义编写实现类(MyUserDetailsService)

原理:编写自定义的实现类UserDetailsService,通过这个类来解析从数据库获取的用户权限相关信息(用户名,用户密码,用户权限),最后在自定义的SpringSecurityConfig配置类中被加载进去

  1. 编写配置类
package com.zhan.demo01.config;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SpringSecurityConfigDemo02 extends WebSecurityConfigurerAdapter {

    @Autowired
//    @Qualifier("userDetailsService")
    private UserDetailsService userDetailsService; //注入自定义的UserDetailsService

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        super.configure(auth);
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);//自定义userDetailsService解析权限信息
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}
  1. 自定义MyUserDetailsService(实现UserDetailsService接口)

源码解析

UserDetailsService接口类

  • 接口中只有一个方法UserDetails loadUserByUsername(String var1),通过varl(指的是用户名字符串)返回一个UserDetails 类

UserDetails类

image-20210821154040000

image-20210821154708823

  • 返回UserDetails的实现类(org\springframework\security\core\userdetails\User.class)对象供SpringSecurityConfig解析加载

自定义UserDetailsService

package com.zhan.demo01.service;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zhan.demo01.bean.User;
import com.zhan.demo01.dao.UserMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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 org.springframework.util.ObjectUtils;

import java.util.List;

@Service(value = "userDetailService")
public class MyUserDetailService implements UserDetailsService {

    private static final Logger logger = LoggerFactory.getLogger(MyUserDetailService.class);

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询包装器
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username);
        User user = userMapper.selectOne(wrapper);
        logger.info("username:{},user:{}", username, user.toString());
        if(ObjectUtils.isEmpty(user)){
            throw new UsernameNotFoundException("用户名或密码错误");
        }
        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList(user.getAuth()); //AuthorityUtils工具类构造权限集合
        //由于同名的关系
        return new org.springframework.security.core.userdetails.User(user.getUsername(), new BCryptPasswordEncoder().encode(user.getPassword()), auths);
    }
}

自定义SpringSecurityConfig配置类

package com.zhan.demo01.config;


import com.zhan.demo01.service.MyUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SpringSecurityConfigDemo02 extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        super.configure(auth);
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();;
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

2. 自定义用户登录页面

配置类

package com.zhan.demo01.config;


import com.zhan.demo01.service.MyUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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 org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SpringSecurityConfigDemo02 extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        super.configure(auth);
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();;
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        super.configure(http);
        http.formLogin() //表单登录
                .loginPage("/login") // 设置登录页面
                .loginProcessingUrl("/login")//设置自定义登录逻辑
                //.defaultSuccessUrl("/success.html",true).permitAll() // 设置登录成功后跳转的页面
                .successForwardUrl("/success") //设置登陆成功跳转页面
                .failureForwardUrl("/error")  //设置登陆失败跳转页面
                .and().authorizeRequests() //对匹配器中的请求进行授权,也就是这些路径不需要权限就能访问
                .antMatchers("/noAuth", "/login","/error").permitAll()
                .anyRequest().authenticated()//任何请求都需要认证
                .and().csrf().disable(); //关闭csrf支持 ,不关闭表单提交会被拦截
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean("userDetailsService")
    public UserDetailsService userDetailsService(){
        return new MyUserDetailService();
    }
}

HelloController

为了跳转方便,这里引入了thymeleaf模板引擎

package com.zhan.demo01.controller;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@Controller
public class HelloController {

    @RequestMapping(value = "/login", method = {RequestMethod.GET, RequestMethod.POST})
    public String toLogin(){
        return "/login";
    }

    @RequestMapping(value = "/index", method = {RequestMethod.GET, RequestMethod.POST})
    public String toIndex(){
        return "/index";
    }

    @GetMapping("/noAuth")
    @ResponseBody
    public String noAuth(){
        return "不需授权页面";
    }

    @RequestMapping(value = "/error", method = {RequestMethod.GET, RequestMethod.POST})
    public String toError(){
        return "/error";
    }

    @RequestMapping(value = "/success", method = {RequestMethod.GET, RequestMethod.POST})
    public String toSuccess(){
        return "/success";
    }
}

静态页面

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
    <form method="post" action="/login">
        用户名: <input name="username" type="text" placeholder="请输入用户名">
        密码: <input name="password" type="text" placeholder="请输入密码">
        <input type="submit" value="提交">
    </form>
</body>
</html>

success.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录成功页面</title>
</head>
<body>
 <h1>登录成功了!</h1>
</body>
</html>

测试

  1. 访问不需要认证的页面

image-20210822172807677

  1. 访问需要授权的页面,会跳转到login.html页面

image-20210822173017199

  1. 输入密码登录后跳转到指定的success.html页面

image-20210822173144971

  1. 登录失败后跳转失败页面

    image-20210822203001137

5. 用户授权

1. 基于权限访问控制

1. hasAuthority()
@Override
protected void configure(HttpSecurity http) throws Exception {
    //        super.configure(http);
    http.formLogin() //表单登录
        .loginPage("/login") // 设置登录页面
        // .loginProcessingUrl("/login")//设置自定义登录逻辑
        //.defaultSuccessUrl("/success.html",true).permitAll() // 设置登录成功后跳转的页面
        .successForwardUrl("/success")
        .failureForwardUrl("/error")
        .and().authorizeRequests() //对匹配器中的请求进行授权,也就是这些路径不需要权限就能访问
        .antMatchers("/noAuth", "/login","/error").permitAll()
        .antMatchers("/index").hasAuthority("admin")
        .anyRequest().authenticated()//任何请求都需要认证
        .and().csrf().disable(); //关闭csrf支持 ,不关闭表单提交会被拦截
}
  • 设置访问的url所需要的权限,如果访问用户所拥有的权限与之不对应,那么会报403访问错误,如果有error.html页面的话,会自动跳转到error页面
2. hasAnyAuthority(“user”,“admin”)
.antMatchers("/index").hasAnyAuthority("user","admin")
  • 可以配置多个权限,访问用户如果拥有其中一个,即可访问

2. 基于角色权限控制

image-20210822221102126

因此如果想要使用基于角色权限控制的话,在**进行角色认证后授权的权限自字符串中要加上前缀“ROLE_”**

1. hasRole()
.antMatchers("/index").hasRole("user") // 配置单个角色
2. hasAnyRole()
.antMatchers("/index").hasAnyRole("user", "admin") //配置多个角色

6. 注解使用

1. @PreAuthorize

@PreAuthorize适合进入方法之前验证授权。 @PreAuthorize可以兼顾,角色/登录用户权限,参数传递给方法等等。

支持El表达式: @PreAuthorize(“hasRole(‘ADMIN’) AND hasRole(‘DBA’)”)

2. @PostAuthorize

@PostAuthorize 虽然不经常使用,检查授权方法之后才被执行,所以它适合用在对返回的值作验证授权。

支持EL表达式: @PostAuthorize(“hasRole(‘ADMIN’) AND hasRole(‘DBA’)”)

3. @Secured

@Secured注释是用来定义业务方法的安全性配置属性列表。您可以使用@Secured在方法上指定安全性要求[角色/权限等],只有对应角色/权限的用户才可以调用这些方法。如果有人试图调用一个方法,但是不拥有所需的角色/权限,那会将会拒绝访问将引发异常。

不支持EL表达式

7. 自动登录(记住我)

1. 流程

image-20210822231206932

2. 源码解析

待补充……

3. 案例测试

1. 数据库配置
  1. 建表
create table persistent_logins (
    username varchar(64) not null, 
    series varchar(64) primary key,
	token varchar(64) not null, 
    last_used timestamp not null
)ENGINE = InnoDB DEFAULT CHARSET = utf8;
  • 也可以通过springSecurity自动创建
  1. 配置数据源
datasource: # 数据源配置
  driver-class-name: com.mysql.cj.jdbc.Driver # 数据驱动
  url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai # 数据库url地址
  username: root # 用户名
  password: root # 密码
2. 配置类(在上面的基础上加入记住我配置)
//注入数据源
@Autowired
private DataSource dataSource;

//配置持久令牌存储库
@Bean
public PersistentTokenRepository persistentTokenRepository(){
    JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
    jdbcTokenRepository.setDataSource(dataSource);
    //自动创建表
    //jdbcTokenRepository.setCreateTableOnStartup(true);
    return jdbcTokenRepository;
}
//记住我设置
http.rememberMe()
    .tokenRepository(persistentTokenRepository()) 
    .tokenValiditySeconds(60) //设置cookie过期时间
    .userDetailsService(userDetailsService);
3. 前端表单加上remember-me的checkbox
<form method="post" action="/login">
    用户名: <input name="username" type="text" placeholder="请输入用户名"><br>
    密码: <input name="password" type="text" placeholder="请输入密码"><br>
    记住我: <input type="checkbox" name="remember-me">
    <input type="submit" value="提交">
</form>
4. 登录认证后,访问页面

可以看到此后在cookie过期的时间内,访问页面的请求头都会带上一个remember-me的cookie,springsecurity会根据传过来的cookie去查询对应的数据库表,如果查得到token且没有过期,那么将会自动登录

image-20210823221629678

image-20210823221956364

8. 注销登录

配置

//在配置类的configure方法中加入
http.logout()//用户注销设置
    .logoutUrl("/logout") //设置注销触发的url
    .logoutSuccessUrl("/login");//注销成功后跳转的页面

9. CSRF功能

1. CSRF是什么?

CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性

2. 如何防御

源码

public final class CsrfFilter extends OncePerRequestFilter {
    public static final RequestMatcher DEFAULT_CSRF_MATCHER = new
            CsrfFilter.DefaultRequiresCsrfMatcher();
    private final Log logger = LogFactory.getLog(this.getClass());
    private final CsrfTokenRepository tokenRepository;
    private RequestMatcher requireCsrfProtectionMatcher;
    private AccessDeniedHandler accessDeniedHandler;
    public CsrfFilter(CsrfTokenRepository csrfTokenRepository) {
        this.requireCsrfProtectionMatcher = DEFAULT_CSRF_MATCHER;
        this.accessDeniedHandler = new AccessDeniedHandlerImpl();
        Assert.notNull(csrfTokenRepository, "csrfTokenRepository cannot be null");
        this.tokenRepository = csrfTokenRepository;
    }
    //通过这里可以看出SpringSecurity的csrf机制把请求方式分成两种类来处理
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        request.setAttribute(HttpServletResponse.class.getName(), response);
        CsrfToken csrfToken = this.tokenRepository.loadToken(request);
        boolean missingToken = csrfToken == null;
        if (missingToken) {
            csrfToken = this.tokenRepository.generateToken(request);
            this.tokenRepository.saveToken(csrfToken, request, response);
        }
        request.setAttribute(CsrfToken.class.getName(), csrfToken);
        request.setAttribute(csrfToken.getParameterName(), csrfToken);
		//"GET", "HEAD", "TRACE", "OPTIONS"四类请求可以直接通过
        if (!this.requireCsrfProtectionMatcher.matches(request)) {
            filterChain.doFilter(request, response);
        } else {
			//除去上面四类,包括POST都要被验证携带token才能通过
            String actualToken = request.getHeader(csrfToken.getHeaderName());
            if (actualToken == null) {
                actualToken = request.getParameter(csrfToken.getParameterName());
            }
            if (!csrfToken.getToken().equals(actualToken)) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Invalid CSRF token found for " +
                            UrlUtils.buildFullRequestUrl(request));
                }
                if (missingToken) {
                    this.accessDeniedHandler.handle(request, response, new
                            MissingCsrfTokenException(actualToken));
                } else {
                    this.accessDeniedHandler.handle(request, response, new
                            InvalidCsrfTokenException(csrfToken, actualToken));
                }
            } else {
                filterChain.doFilter(request, response);
            }
        }
    }
    public void setRequireCsrfProtectionMatcher(RequestMatcher requireCsrfProtectionMatcher) {
        Assert.notNull(requireCsrfProtectionMatcher, "requireCsrfProtectionMatcher cannot be
        null");
        this.requireCsrfProtectionMatcher = requireCsrfProtectionMatcher;
    }
    public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
        Assert.notNull(accessDeniedHandler, "accessDeniedHandler cannot be null");
        this.accessDeniedHandler = accessDeniedHandler;
    }
    private static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
        private final HashSet<String> allowedMethods;
        private DefaultRequiresCsrfMatcher() {
            this.allowedMethods = new HashSet(Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS"));
    }
    public boolean matches(HttpServletRequest request) {
            return !this.allowedMethods.contains(request.getMethod());
        }
    }
}
3. 禁用CSRF
http
    //关闭打开的csrf保护
    .csrf().disable();
4. Csrf Token

用户登录时,系统发放一个CsrfToken值,用户携带该CsrfToken值与用户名、密码等参数完成登录。系统记录该会话的 CsrfToken 值,之后在用户的任何请求中,都必须带上该CsrfToken值,并由系统进行校验。这种方法需要与前端配合,包括存储CsrfToken值,以及在任何请求中(包括表单和Ajax)携带CsrfToken值。

  • 优点:安全性相较于HTTP Referer提高很多,如果都是XMLHttpRequest,则可以统一添加CsrfToken值;
  • 缺点:存在大量的表单和a标签,就会变得非常烦琐.
5. 使用CSRF

HttpSessionCsrfTokenRepository

在默认情况下,Spring Security加载的是一个HttpSessionCsrfTokenRepository
HttpSessionCsrfTokenRepository 将 CsrfToken 值存储在 HttpSession 中,并指定前端把CsrfToken 值放在名为“csrf”的请求参数或名为“X-CSRF-TOKEN”的请求头字段里(可以调用相应的设置方法来重新设定)。校验时,通过对比HttpSession内存储的CsrfToken值与前端携带的CsrfToken值是否一致,便能断定本次请求是否为CSRF攻击

<input type='hidden' th:name='${_csrf.parameterName}' th:value='${_csrf.token}'>

注:这里单纯这样的html页面是无法获取请求域的值的,需要配合模板引擎(thymeleaf)或其他前端框架

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值