• 【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"; // 重定向要写/标识 区分模版解析
        }
    }
  • 相关阅读:
    Redis和Memcache的区别
    j2EE框架collection
    总结乐观锁和悲观锁
    lunix,命令集锦
    遍历Map集合的方法
    arrayList和vector的区别
    python借助zookeeper实现分布式服务(二)
    python借助zookeeper实现分布式服务(一)
    zookeeper常用命令
    python实现事件驱动模型
  • 原文地址:https://www.cnblogs.com/mindzone/p/13742523.html
Copyright © 2020-2023  润新知