【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"; // 重定向要寫/標識 區分模版解析  }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM