zoukankan      html  css  js  c++  java
  • 【Spring-Security】Re03 认证参数修改与跨域跳转处理

    一、请求参数名设置

    之前的表单信息有一些要求:

    1、action属性发送的地址是Security设置的URL

    2、发送的请求方式是POST

    3、请求的账户信息,也就是表单发送的参数,必须对应的是username & password

    原因是因为这个过滤器类:

    org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

    和下面的方法:

    如果要改变默认的请求参数,可以设置:

    package cn.zeal4j.configuration;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    /**
     * @author Administrator
     * @file IntelliJ IDEA Spring-Security-Tutorial
     * @create 2020 09 27 21:55
     */
    @Configuration
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @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请求
    
            httpSecurity.authorizeRequests().
                    antMatchers("/login.html").permitAll(). // 登陆页面允许任意访问
                    antMatchers("/error.html").permitAll(). // 失败跳转后重定向的页面也需要被允许访问
                    anyRequest().authenticated(); // 其他请求均需要被授权访问
    
            // CSRF攻击拦截关闭
            httpSecurity.csrf().disable();
        }
    }

    二、前后端分离登陆成功页的跳转:

    问题的产生:

    使用默认successForwardUrl并不能用来跳转到外部地址,也就是跨域访问

    例如这样设置成功登陆页进行跳转:

    successForwardUrl("http://www.baidu.com")

    方法分析:

    successForwardUrl方法的实现:

    public FormLoginConfigurer<H> successForwardUrl(String forwardUrl) {
        this.successHandler(new ForwardAuthenticationSuccessHandler(forwardUrl));
        return this;
    }

    内部是调用了一个成功处理器方法,并且注入了一个跳转授权成功处理器对象,构造参数又是这个要登录的页面URL

    跳转授权成功处理器类:

    package org.springframework.security.web.authentication;
    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.web.util.UrlUtils;
    import org.springframework.util.Assert;
    
    public class ForwardAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
        private final String forwardUrl;
    
        public ForwardAuthenticationSuccessHandler(String forwardUrl) {
            Assert.isTrue(UrlUtils.isValidRedirectUrl(forwardUrl), () -> {
                return "'" + forwardUrl + "' is not a valid forward URL";
            });
            this.forwardUrl = forwardUrl;
        }
    
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            request.getRequestDispatcher(this.forwardUrl).forward(request, response);
        }
    }

    可以看到下面的这个方法是将URL进行转发处理的,所以像跨域处理的是没有办法解决的

    这个处理器类是实现了一个接口:

    org.springframework.security.web.authentication.AuthenticationSuccessHandler 

    接口的方法:

    package org.springframework.security.web.authentication;
    import java.io.IOException;
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.security.core.Authentication;
    
    public interface AuthenticationSuccessHandler {
        default void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
            this.onAuthenticationSuccess(request, response, authentication);
            chain.doFilter(request, response);
        }
    
        void onAuthenticationSuccess(HttpServletRequest var1, HttpServletResponse var2, Authentication var3) throws IOException, ServletException;
    }

    也就是说,我们可以通过自己写一个类实现这个接口,单独处理前后端分离的重定向登陆URL

    package cn.zeal4j.handler;
    
    import org.springframework.security.core.Authentication;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * @author Administrator
     * @file IntelliJ IDEA Spring-Security-Tutorial
     * @create 2020 09 27 23:48
     * 前后端分离url登陆处理 Front and rear separation (FARS)
     */
    public class FarsAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    
        private String url;
    
        public FarsAuthenticationSuccessHandler(String url) {
            this.url = url;
        }
    
        @Override
        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
            httpServletResponse.sendRedirect(url);
        }
    
    //    @Override
    //    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
    //
    //    }
    }

    改写登陆成功页面URL的方法:

    package cn.zeal4j.configuration;
    
    import cn.zeal4j.handler.FarsAuthenticationSuccessHandler;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    /**
     * @author Administrator
     * @file IntelliJ IDEA Spring-Security-Tutorial
     * @create 2020 09 27 21:55
     */
    @Configuration
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @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请求
                    successHandler(new FarsAuthenticationSuccessHandler("https://www.acfun.cn/")). // 使用自定义的重定向登陆
                    failureForwardUrl("/error.page");  // 设置认证失败跳转URL路径 POST请求
    
            httpSecurity.authorizeRequests().
                    antMatchers("/login.html").permitAll(). // 登陆页面允许任意访问
                    antMatchers("/error.html").permitAll(). // 失败跳转后重定向的页面也需要被允许访问
                    anyRequest().authenticated(); // 其他请求均需要被授权访问
    
            // CSRF攻击拦截关闭
            httpSecurity.csrf().disable();
        }
    }

    参数Authentication可以获取登陆账户的信息:

    package cn.zeal4j.handler;
    
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.Collection;
    
    /**
     * @author Administrator
     * @file IntelliJ IDEA Spring-Security-Tutorial
     * @create 2020 09 27 23:48
     * 前后端分离url登陆处理 Front and rear separation (FARS)
     */
    public class FarsAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    
        private String url;
    
        public FarsAuthenticationSuccessHandler(String url) {
            this.url = url;
        }
    
        @Override
        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
            
            Object principal = authentication.getPrincipal();
            User user = (User) principal;
            Collection<GrantedAuthority> authorities = user.getAuthorities();
            System.out.println(user.getUsername());
            System.out.println(user.getPassword());
            for (GrantedAuthority authority : authorities) {
                System.out.println(authority.getAuthority());
            }
    
            httpServletResponse.sendRedirect(url);
        }
    
    //    @Override
    //    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
    //
    //    }
    }

    打印结果:

    admin
    null
    admin
    normal

    密码输出NULL是因为Security的保护限制?

    三、前后端分离登陆失败页的跳转:

    有成功页面的跳转,对应的就有失败页面的跳转

    public FormLoginConfigurer<H> failureForwardUrl(String forwardUrl) {
        this.failureHandler(new ForwardAuthenticationFailureHandler(forwardUrl));
        return this;
    }
    

    也是同样的方法和类似的对象和相同的注入方式

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by FernFlower decompiler)
    //
    
    package org.springframework.security.web.authentication;
    
    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.util.UrlUtils;
    import org.springframework.util.Assert;
    
    public class ForwardAuthenticationFailureHandler implements AuthenticationFailureHandler {
        private final String forwardUrl;
    
        public ForwardAuthenticationFailureHandler(String forwardUrl) {
            Assert.isTrue(UrlUtils.isValidRedirectUrl(forwardUrl), () -> {
                return "'" + forwardUrl + "' is not a valid forward URL";
            });
            this.forwardUrl = forwardUrl;
        }
    
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
            request.setAttribute("SPRING_SECURITY_LAST_EXCEPTION", exception);
            request.getRequestDispatcher(this.forwardUrl).forward(request, response);
        }
    }

    所以要实现跨域跳转,一样是实现上面的接口,除了跳转,该方法还要求携带异常对象

    自定义实现类

    package cn.zeal4j.handler;
    
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.authentication.AuthenticationFailureHandler;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * @author Administrator
     * @file IntelliJ IDEA Spring-Security-Tutorial
     * @create 2020 09 28 3:44
     */
    public class FarsAuthenticationFailureHandler implements AuthenticationFailureHandler {
    
        private String url;
    
        public FarsAuthenticationFailureHandler(String url) {
            this.url = url;
        }
    
        @Override
        public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
            // 因为跨域,无法携带异常对象
            httpServletResponse.sendRedirect(url);
        }
    }

    更改Security跳转配置:

    package cn.zeal4j.configuration;
    
    import cn.zeal4j.handler.FarsAuthenticationFailureHandler;
    import cn.zeal4j.handler.FarsAuthenticationSuccessHandler;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    /**
     * @author Administrator
     * @file IntelliJ IDEA Spring-Security-Tutorial
     * @create 2020 09 27 21:55
     */
    @Configuration
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @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请求
                    successHandler(new FarsAuthenticationSuccessHandler("https://www.acfun.cn/")). // 使用自定义的重定向登陆
                    // failureForwardUrl("/error.page");  // 设置认证失败跳转URL路径 POST请求
                    failureHandler(new FarsAuthenticationFailureHandler("/error.html")); // 跨域处理,不需要跳转了
    
            httpSecurity.authorizeRequests().
                    antMatchers("/login.html").permitAll(). // 登陆页面允许任意访问
                    antMatchers("/error.html").permitAll(). // 失败跳转后重定向的页面也需要被允许访问
                    anyRequest().authenticated(); // 其他请求均需要被授权访问
    
            // CSRF攻击拦截关闭
            httpSecurity.csrf().disable();
        }
    }

    登陆控制器的跳转方法就不需要了

    package cn.zeal4j.controller;
    
    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
     */
    
    @Controller
    public class LoginController {
    
        @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"; // 重定向要写/标识 区分模版解析
        }
    }
  • 相关阅读:
    为什么 PCB 生产时推荐出 Gerber 给工厂?
    Fedora Redhat Centos 有什么区别和关系?
    【KiCad】 如何给元件给元件的管脚加上划线?
    MCU ADC 进入 PD 模式后出现错误的值?
    FastAdmin 生产环境升级注意
    EMC EMI 自行评估记录
    如何让你的 KiCad 在缩放时不眩晕?
    KiCad 5.1.0 正式版终于发布
    一次单片机 SFR 页引发的“事故”
    java基础之集合
  • 原文地址:https://www.cnblogs.com/mindzone/p/13742523.html
Copyright © 2011-2022 走看看