欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot使用Filter實現(xiàn)簽名認(rèn)證鑒權(quán)的示例代碼

 更新時間:2021年04月27日 11:44:54   作者:justry_deng  
這篇文章主要介紹了SpringBoot使用Filter實現(xiàn)簽名認(rèn)證鑒權(quán)的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

情景說明

        鑒權(quán),有很多方案,如:SpringSecurity、Shiro、攔截器、過濾器等等。如果只是對一些URL進行認(rèn)證鑒權(quán)的話,我們完
全沒必要引入SpringSecurity或Shiro等框架,使用攔截器或過濾器就足以實現(xiàn)需求。
        本文介紹如何使用過濾器Filter實現(xiàn)URL簽名認(rèn)證鑒權(quán)。

本人測試軟硬件環(huán)境:Windows10、Eclipse、SpringBoot、JDK1.8

準(zhǔn)備工作

 第一步:在pom.xml中引入相關(guān)依賴

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter</artifactId>
	</dependency>
 
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
 
	<!-- web -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
 
	<!-- devtools -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-devtools</artifactId>
	</dependency>
 
	<!-- org.apache.commons.codec -->
	<!-- MD5加密的依賴 -->
	<dependency>
		<groupId>org.apache.directory.studio</groupId>
		<artifactId>org.apache.commons.codec</artifactId>
		<version>1.8</version>
	</dependency>
</dependencies>

第二步:在系統(tǒng)配置文件application.properties中配置相關(guān)參數(shù),一會兒代碼中需要用到

# ip白名單(多個使用逗號分隔)
permitted-ips = 169.254.205.177, 169.254.133.33, 10.8.109.31, 0:0:0:0:0:0:0:1
# secret
secret = JustryDeng

第三步:準(zhǔn)備獲取客戶端IP的工具類

import java.net.InetAddress;
import java.net.UnknownHostException;
 
import javax.servlet.http.HttpServletRequest;
 
/**
 * 獲取發(fā)出request請求的客戶端ip
 * 注:如果是自己發(fā)出的請求,那么獲取的是自己的ip
 * 摘錄自https://blog.csdn.net/byy8023/article/details/80499038
 * 
 * 注意事項:
 *    如果使用此工具,獲取到的不是客戶端的ip地址;而是虛擬機的ip地址(d當(dāng)客戶端安裝有VMware時,可能出現(xiàn)此情況);
 *    那么需要在客戶端的[控制面板\網(wǎng)絡(luò)和 Internet\網(wǎng)絡(luò)連接]中禁用虛擬機網(wǎng)絡(luò)適配器
 *
 * @author JustryDeng
 * @DATE 2018年9月10日 下午8:56:48
 */
public class IpUtil {
	
    public static String getIpAddr(HttpServletRequest request) {
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals("127.0.0.1")) {
                    // 根據(jù)網(wǎng)卡取本機配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    ipAddress = inet.getHostAddress();
                }
            }
            // 對于通過多個代理的情況,第一個IP為客戶端真實IP,多個IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
                                                                // = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress="";
        }
        
        return ipAddress;
    }
}

第四步:準(zhǔn)備MD5加密工具類

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Hex;
 
/**
 * MD5加密工具類
 *
 * @author JustryDeng 參考自ShaoJJ的MD5加密工具類  
 * @DATE 2018年9月11日 下午2:14:21
 */
public class MDUtils {
 
	/**
	 * 加密
	 *
	 * @param origin
	 *            要被加密的字符串
	 * @param charsetname
	 *            加密字符,如UTF-8
	 * @DATE 2018年9月11日 下午2:12:51
	 */
	public static String MD5EncodeForHex(String origin, String charsetname) 
			throws UnsupportedEncodingException, NoSuchAlgorithmException {
		return MD5EncodeForHex(origin.getBytes(charsetname));
	}
 
	public static String MD5EncodeForHex(byte[] origin) throws NoSuchAlgorithmException {
		return Hex.encodeHexString(digest("MD5", origin));
	}
 
	/**
	 * 指定加密算法
	 *
	 * @throws NoSuchAlgorithmException
	 * @DATE 2018年9月11日 下午2:11:58
	 */
	private static byte[] digest(String algorithm, byte[] source) throws NoSuchAlgorithmException {
		MessageDigest md;
		md = MessageDigest.getInstance(algorithm);
		return md.digest(source);
	}
}

第五步:簡單編寫一個Controller,方便后面的測試

  SpringBoot使用Filter實現(xiàn)簽名認(rèn)證鑒權(quán) --- 邏輯代碼

第一步:編寫過濾器

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
 
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
 
import com.aspire.util.IpUtil;
import com.aspire.util.MDUtils;
 
/**
 * SpringBoot使用攔截器實現(xiàn)簽名認(rèn)證(鑒權(quán))
 * @WebFilter注解指定要被過濾的URL
 * 一個URL會被多個過濾器過濾時,還可以使用@Order(x)來指定過濾request的先后順序,x數(shù)字越小越先過濾
 * 
 * @author JustryDeng
 * @DATE 2018年9月11日 下午1:18:29
 */
@WebFilter(urlPatterns = { "/authen/test1", "/authen/test2", "/authen/test3"})
public class SignAutheFilter implements Filter {
 
	private static Logger logger = LoggerFactory.getLogger(SignAutheFilter.class);
 
	@Value("${permitted-ips}")
	private String[] permittedIps;
 
	@Value("${secret}")
	private String secret;
	
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
	}
 
	@Override
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
		try {
			String authorization = request.getHeader("Authorization");
			logger.info("getted Authorization is ---> " + authorization);
			String[] info = authorization.split(",");
 
			// 獲取客戶端ip
			String ip = IpUtil.getIpAddr(request);
			logger.info("getted ip is ---> " + ip);
			
			/* 
			 * 讀取請求體中的數(shù)據(jù)(字符串形式)
			 * 注:由于同一個流不能讀取多次;如果在這里讀取了請求體中的數(shù)據(jù),那么@RequestBody中就不能讀取到了
			 *    會拋出異常并提示getReader() has already been called for this request
			 * 解決辦法:先將讀取出來的流數(shù)據(jù)存起來作為一個常量屬性.然后每次讀的時候,都需要先將這個屬性值寫入,再讀出.
			 *        即每次獲取的其實是不同的流,但是獲取到的數(shù)據(jù)都是一樣的.
			 *        這里我們借助HttpServletRequestWrapper類來實現(xiàn)
			 *      注:此方法涉及到流的讀寫、耗性能;
			 */
			MyRequestWrapper mrw = new MyRequestWrapper(request);
			String bodyString = mrw.getBody();
			logger.info("getted requestbody data is ---> " + bodyString);
			
			// 獲取幾個相關(guān)的字符
			// 由于authorization類似于
			// cardid="1234554321",timestamp="9897969594",signature="a69eae32a0ec746d5f6bf9bf9771ae36"
			// 這樣的,所以邏輯是下面這樣的
			int cardidIndex = info[0].indexOf("=") + 2;
			String cardid = info[0].substring(cardidIndex, info[0].length() - 1);
			logger.info("cardid is ---> " + cardid);
			int timestampIndex = info[1].indexOf("=") + 2;
			String timestamp = info[1].substring(timestampIndex, info[1].length() - 1);
			int signatureIndex = info[2].indexOf("=") + 2;
			String signature = info[2].substring(signatureIndex, info[2].length() - 1);
			String tmptString = MDUtils.MD5EncodeForHex(timestamp + secret + bodyString, "UTF-8")
					                .toUpperCase();
			logger.info("getted ciphertext is ---> {}, correct ciphertext is ---> {}", 
					       signature , tmptString);
 
			// 判斷該ip是否合法
			boolean containIp = false;
			for (String string : permittedIps) {
				if (string.equals(ip)) {
					containIp = true;
					break;
				}
			}
 
			// 再判斷Authorization內(nèi)容是否正確,進而判斷是否最終放行
			boolean couldPass = containIp && tmptString.equals(signature);
			if (couldPass) {
				// 放行
				chain.doFilter(mrw, response);
				return;
			}
			response.sendError(403, "Forbidden");
		} catch (Exception e) {
			logger.error("AxbAuthenticationFilter -> " + e.getMessage(), e);
			response.sendError(403, "Forbidden");
		}
	}
 
	@Override
	public void destroy() {
 
	}
 
}
 
/**
 * 輔助類 ---> 變相使得可以多次通過(不同)流讀取相同數(shù)據(jù)
 *
 * @author JustryDeng
 * @DATE 2018年9月11日 下午7:13:52
 */
class MyRequestWrapper extends HttpServletRequestWrapper {
 
    private final String body;
 
    public String getBody() {
		return body;
	}
 
	public MyRequestWrapper(final HttpServletRequest request) throws IOException {
        super(request);
        StringBuilder sb = new StringBuilder();
        String line;
        BufferedReader reader = request.getReader();
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
 
        body = sb.toString();
    }
 
    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes());
        return new ServletInputStream() {
            /*
             * 重寫ServletInputStream的父類InputStream的方法
             */
            @Override
            public int read() throws IOException {
                return bais.read();
            }
            
			@Override
			public boolean isFinished() {
				return false;
			}
 
			@Override
			public boolean isReady() {
				return false;
			}
 
			@Override
			public void setReadListener(ReadListener listener) {	
			}
        };
    }
 
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
}

第二步:在項目的啟動類上添加@ServletComponentScan注解,使允許掃描

            Servlet組件(過濾器、監(jiān)聽器等)。

測試一下

測試說明

    客戶端ip在我們設(shè)置的ip白名單里面 且 timestamp + secret + bodyStringMD5加密后的字段與請求頭域中傳過來的signature值相同時,才算鑒權(quán)通過。

說明:

        1.ip白名單 本示例中是設(shè)置在服務(wù)端的相應(yīng)服務(wù)的系統(tǒng)配置文件application.properties中的。
        2.secret 是客戶端一方和服務(wù)端一方 定好的一個用來MD5加密的   數(shù),secret本身不進行傳輸。
        3.bodyString是服務(wù)端通過客戶端的request獲取到的請求體中的數(shù)據(jù)。
        4.signature是客戶端加密后的值,服務(wù)端只需對原始數(shù)據(jù)進行和客戶端進一模一樣的加密,
           將加密結(jié)果和傳導(dǎo)服務(wù)端的signature進行比對,一樣則鑒權(quán)通過。

啟動項目,使用postman測試一下

給出程序打印的日志,更容易理解

提示:由于本人測試時,我的電腦既是服務(wù)器又是客戶端,所以獲取到了那樣的ip。

注:當(dāng)ip或Authorization值中任意一個或兩個 不滿足條件時,會返回給前端403(見:SignAutheFilter中的相關(guān)代碼),
     這里就不給出效果圖了。

由測試結(jié)果可知:簽名鑒權(quán)成功!

測試項目代碼托管鏈接: https://github.com/JustryDeng/PublicRepository ​

到此這篇關(guān)于SpringBoot使用Filter實現(xiàn)簽名認(rèn)證鑒權(quán)的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot Filter簽名認(rèn)證鑒權(quán)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • IDEA SpringBoot:Cannot resolve configuration property配置文件問題

    IDEA SpringBoot:Cannot resolve configuration&

    這篇文章主要介紹了IDEA SpringBoot:Cannot resolve configuration property配置文件問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • 在Java中如何比較兩個對象淺析

    在Java中如何比較兩個對象淺析

    在工作中我們經(jīng)常會遇到這樣的需求——比較兩個對象是否相等,如果不相等的話,取出不相等的字段,這篇文章主要給大家介紹了關(guān)于在Java中如何比較兩個對象的相關(guān)資料,需要的朋友可以參考下
    2021-11-11
  • RocketMQ消息中間件超詳細解讀

    RocketMQ消息中間件超詳細解讀

    這篇文章主要介紹了RocketMQ消息中間件超詳細解讀,RocketMQ作為一款純java、分布式、隊列模型的開源消息中間件,支持事務(wù)消息、順序消息、批量消息、定時消息、消息回溯等,本文就來詳細解讀一下,需要的朋友可以參考下
    2023-05-05
  • JVM調(diào)優(yōu)實戰(zhàn)

    JVM調(diào)優(yōu)實戰(zhàn)

    本文主要介紹了JVM調(diào)優(yōu)實戰(zhàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • mybatis執(zhí)行update批量更新時報錯的解決方案

    mybatis執(zhí)行update批量更新時報錯的解決方案

    這篇文章主要介紹了mybatis執(zhí)行update批量更新時報錯的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java中的事件處理機制詳細解讀

    Java中的事件處理機制詳細解讀

    這篇文章主要介紹了Java中的事件處理機制詳細解讀,ava事件處理是采取"委派事件模型",當(dāng)事件發(fā)生時,產(chǎn)生事件的對象會把此"信息"傳遞給"事件的監(jiān)聽者"處理,需要的朋友可以參考下
    2024-01-01
  • JDBC的ResultSet使用說明

    JDBC的ResultSet使用說明

    今天小編就為大家分享一篇JDBC的ResultSet使用說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-02-02
  • springboot攔截器不攔截靜態(tài)資源,只攔截controller的實現(xiàn)方法

    springboot攔截器不攔截靜態(tài)資源,只攔截controller的實現(xiàn)方法

    這篇文章主要介紹了springboot攔截器不攔截靜態(tài)資源,只攔截controller的實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • java?Export大量數(shù)據(jù)導(dǎo)出和打包

    java?Export大量數(shù)據(jù)導(dǎo)出和打包

    這篇文章主要為大家介紹了java?Export大量數(shù)據(jù)的導(dǎo)出和打包實現(xiàn)過程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • MyBatis自定義類型轉(zhuǎn)換器實現(xiàn)加解密

    MyBatis自定義類型轉(zhuǎn)換器實現(xiàn)加解密

    這篇文章主要介紹了MyBatis自定義類型轉(zhuǎn)換器實現(xiàn)加解密的相關(guān)資料,需要的朋友可以參考下
    2016-07-07

最新評論