• 【Spring-Security】Re09 CSFR处理


    一、CSRF:

    CSRF 全称 Cross Site Request Forgery 跨站请求伪造

    又称为OneClick Attack & SessionRiding 

    是非法请求访问,通过伪造用户请求访问受信任网站

    什么是跨域?

    只要 协议 - IP地址 - 端口号 三者中的任何一个不相同进行的访问,就是跨域请求

    相关资料:

    https://blog.csdn.net/xiaoxinshuaiga/article/details/80766369

    二、Security的CSRF设置:

    Security4版本开始默认开启CSRF攻击拦截,验证的方法是要求凭证提供键为 _csrf 的ID

    该Token在服务端生成,登录时会用户携带此ID一并给Security。

    演示案例:

    首先取消CSRF关闭:

    package cn.zeal4j.configuration;
    
    import cn.zeal4j.handler.CustomAccessDeniedHandler;
    import cn.zeal4j.handler.FarsAuthenticationFailureHandler;
    import cn.zeal4j.handler.FarsAuthenticationSuccessHandler;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpMethod;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.core.parameters.P;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.access.AccessDeniedHandler;
    import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
    import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
    
    import javax.sql.DataSource;
    
    /**
     * @author Administrator
     * @file IntelliJ IDEA Spring-Security-Tutorial
     * @create 2020 09 27 21:55
     */
    @Configuration
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private AccessDeniedHandler accessDeniedHandler;
        @Qualifier("userDetailsServiceImpl")
        @Autowired
        private UserDetailsService userDetailsService;
        @Autowired
        private DataSource dataSource;
        @Autowired
        private PersistentTokenRepository persistentTokenRepository;
    
        @Bean
        public PersistentTokenRepository getPersistentTokenRepository() {
            JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
            jdbcTokenRepository.setDataSource(dataSource); // 数据源注入
            jdbcTokenRepository.setCreateTableOnStartup(false); // 由Security完成Token表的创建,如果有了就设置false关闭
            return jdbcTokenRepository;
        }
    
        @Bean
        public PasswordEncoder getPasswordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
            httpSecurity.formLogin(). // 设置登陆行为方式为表单登陆
                    // 登陆请求参数设置
                    usernameParameter("username").
                    passwordParameter("password").
    
                    loginPage("/login.html"). // 设置登陆页面URL路径
                    loginProcessingUrl("/login.action"). // 设置表单提交URL路径
    
                    successForwardUrl("/main.page"). // 设置认证成功跳转URL路径 POST请求
                    failureForwardUrl("/error.page");  // 设置认证失败跳转URL路径 POST请求
    
                    //  successHandler(new FarsAuthenticationSuccessHandler("https://www.acfun.cn/")). // 使用自定义的重定向登陆
                    //  failureHandler(new FarsAuthenticationFailureHandler("/error.html")).; // 跨域处理,不需要跳转了
    
            httpSecurity.authorizeRequests().
                    regexMatchers(HttpMethod.POST, "正则表达式").permitAll(). // 还可以对符合正则表达式的请求方式进行要求,这个属性使用来制定请求的方式
    
                    antMatchers("/**/*.js", "/**/*.css", "/**/images/*.*").permitAll(). // 静态资源放行
    
                    antMatchers("/login.html").permitAll(). // 登陆页面允许任意访问
                    antMatchers("/error.html").permitAll(). // 失败跳转后重定向的页面也需要被允许访问
                    antMatchers("/admin.page").hasAnyAuthority("admin").
    
                    /*antMatchers("/vip-01.page").hasAnyAuthority("vip-01").*/
                    antMatchers("/vip-01.page").hasRole("vip-01").
                    antMatchers("/ip.page").hasIpAddress("192.168.43.180").
    
                    // mvcMatchers("/main.page").servletPath("/xxx").permitAll(). // mvcMatchers资源放行匹配
                    // antMatchers("/xxx/main.page").permitAll(). // 或者多写MSP的前缀
    
                    anyRequest().authenticated(); // 其他请求均需要被授权访问
                    // anyRequest().access("@customServiceImpl.hasPermission(request, authentication)"); // 自定义Access配置
    
            // CSRF攻击拦截关闭
            // httpSecurity.csrf().disable();
    
            httpSecurity.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
    
    
            // 记住我
            httpSecurity.rememberMe().
                    tokenValiditySeconds(60). // 设置Token有效时间, 以秒为单位取值
                    userDetailsService(userDetailsService).
                    tokenRepository(persistentTokenRepository);
    
            // 退出登录处理
            httpSecurity.
                    logout().
                    // logoutUrl("/xxx/xxx/logout").
                    logoutSuccessUrl("/login.html");
        }
    }

    注意CSRF的Token需要Thymeleaf模板获取,也就是登录页面要放在模板目录里面,这里就重新编写一个登陆页面【csrf-login.html】

    在登陆页面设置CSRF的Token处理:

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org" >
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style type="text/css">
            h3,p {
                text-align: center;
            }
        </style>
    </head>
    <body>
        <h3>custom login page</h3>
        <form method="post"th:action="@{/login.action}" >
            <input type="hidden" th:value="${_csrf.token}" name="_csrf" th:if="${_csrf}" >
            <p>username: <input type="text" name="username"></p>
            <p>password: <input type="password" name="password"></p>
            <p>rememberMe: <input type="checkbox" name="remember-me" value="true"></p>
            <p><input type="submit" value="login"></p>
        </form>
    </body>
    </html>

    编写控制器

    package cn.zeal4j.controller;
    
    import org.springframework.security.access.annotation.Secured;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * @author Administrator
     * @file IntelliJ IDEA Spring-Security-Tutorial
     * @create 2020 09 27 22:35
     */
    @Secured("ROLE_vip-01")
    @Controller
    public class LoginController {
        
        @PostMapping("csrf-login.page")
        public String toCsrfLoginPage() {
            return "csrf-login";
        }
    
        @RequestMapping("main.page")
        public String toMainPage() {
            return "main"; // 模版内的页面不允许重定向,忘了忘了
        }
    
    
    
        @PostMapping("error.page") // 控制器不支持POST请求跳转解析, 需要控制器跳转 Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
        public String redirectToErrorPage() {
            return "redirect:/error.html"; // 重定向要写/标识 区分模版解析
        }
    
    
    }

    配置再更改为这个控制接口:

    package cn.zeal4j.configuration;
    
    import cn.zeal4j.handler.CustomAccessDeniedHandler;
    import cn.zeal4j.handler.FarsAuthenticationFailureHandler;
    import cn.zeal4j.handler.FarsAuthenticationSuccessHandler;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpMethod;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.core.parameters.P;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.access.AccessDeniedHandler;
    import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
    import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
    
    import javax.sql.DataSource;
    
    /**
     * @author Administrator
     * @file IntelliJ IDEA Spring-Security-Tutorial
     * @create 2020 09 27 21:55
     */
    @Configuration
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private AccessDeniedHandler accessDeniedHandler;
        @Qualifier("userDetailsServiceImpl")
        @Autowired
        private UserDetailsService userDetailsService;
        @Autowired
        private DataSource dataSource;
        @Autowired
        private PersistentTokenRepository persistentTokenRepository;
    
        @Bean
        public PersistentTokenRepository getPersistentTokenRepository() {
            JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
            jdbcTokenRepository.setDataSource(dataSource); // 数据源注入
            jdbcTokenRepository.setCreateTableOnStartup(false); // 由Security完成Token表的创建,如果有了就设置false关闭
            return jdbcTokenRepository;
        }
    
        @Bean
        public PasswordEncoder getPasswordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
            httpSecurity.formLogin(). // 设置登陆行为方式为表单登陆
                    // 登陆请求参数设置
                    usernameParameter("username").
                    passwordParameter("password").
    
                    // loginPage("/login.html"). // 设置登陆页面URL路径
                    loginPage("/csrf-login.page").
                    loginProcessingUrl("/login.action"). // 设置表单提交URL路径
    
                    successForwardUrl("/main.page"). // 设置认证成功跳转URL路径 POST请求
                    failureForwardUrl("/error.page");  // 设置认证失败跳转URL路径 POST请求
    
                    //  successHandler(new FarsAuthenticationSuccessHandler("https://www.acfun.cn/")). // 使用自定义的重定向登陆
                    //  failureHandler(new FarsAuthenticationFailureHandler("/error.html")).; // 跨域处理,不需要跳转了
    
            httpSecurity.authorizeRequests().
                    regexMatchers(HttpMethod.POST, "正则表达式").permitAll(). // 还可以对符合正则表达式的请求方式进行要求,这个属性使用来制定请求的方式
    
                    antMatchers("/**/*.js", "/**/*.css", "/**/images/*.*").permitAll(). // 静态资源放行
    
                    antMatchers("/csrf-login.page").permitAll().
                    antMatchers("/login.html").permitAll(). // 登陆页面允许任意访问
                    antMatchers("/error.html").permitAll(). // 失败跳转后重定向的页面也需要被允许访问
                    antMatchers("/admin.page").hasAnyAuthority("admin").
    
                    /*antMatchers("/vip-01.page").hasAnyAuthority("vip-01").*/
                    antMatchers("/vip-01.page").hasRole("vip-01").
                    antMatchers("/ip.page").hasIpAddress("192.168.43.180").
    
                    // mvcMatchers("/main.page").servletPath("/xxx").permitAll(). // mvcMatchers资源放行匹配
                    // antMatchers("/xxx/main.page").permitAll(). // 或者多写MSP的前缀
    
                    anyRequest().authenticated(); // 其他请求均需要被授权访问
                    // anyRequest().access("@customServiceImpl.hasPermission(request, authentication)"); // 自定义Access配置
    
            // CSRF攻击拦截关闭
            // httpSecurity.csrf().disable();
    
            httpSecurity.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
    
    
            // 记住我
            httpSecurity.rememberMe().
                    tokenValiditySeconds(60). // 设置Token有效时间, 以秒为单位取值
                    userDetailsService(userDetailsService).
                    tokenRepository(persistentTokenRepository);
    
            // 退出登录处理
            httpSecurity.
                    logout().
                    // logoutUrl("/xxx/xxx/logout").
                    logoutSuccessUrl("/login.html");
        }
    }

    使用csrf登录:

    CSRF的显示在这里:

  • 相关阅读:
    Java 处理 iphone拍照后 图片EXIF属性翻转90度的方法
    spring boot文件上传、下载
    python dict.get()和dict['key']的区别
    python zip()
    Pythonn new-style class and old-style class
    mysql错误
    django 模板中url的处理
    python中isort的使用
    使用uWSGI部署django项目
    django处理静态文件
  • 原文地址:https://www.cnblogs.com/mindzone/p/13748688.html
Copyright © 2020-2023  润新知