SpringBoot Filterを使用して署名認証認証権を実現する例示的なコード


情景説明
        鑑権はいろいろな案があります。例えば、Spring Security、Shro、傍受、フィルターなどです。いくつかのURLだけを認証したら、私達は終わります。
Spring SecurityやShroなどのフレームを導入する必要は全くありません。スクリーンやフィルターを使って需要を実現します。
        ここでは、フィルタFilterを使用してURL署名認証認証を行う方法を紹介します。
本人テストソフトハードウェア環境:Windows 10、Eclipse、Spring Boot、JDK 1.8
準備工作
 第一歩:pom.xmlに関連する依存性を導入する

<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>
第二ステップ:システムプロファイルappication.propertiesに関連パラメータを設定し、コードの中で使用する必要があります。

# 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
ステップ3:クライアントIPを取得するためのツールクラス

import java.net.InetAddress;
import java.net.UnknownHostException;
 
import javax.servlet.http.HttpServletRequest;
 
/**
 *     request      ip
 *  :          ,         ip
 *    https://blog.csdn.net/byy8023/article/details/80499038
 * 
 *     :
 *           ,          ip  ;      ip  (d       VMware ,       );
 *             [    \    Internet\    ]           
 *
 * @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")) {
                    //           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;
    }
}
第四ステップ:MD 5暗号化ツール類の準備

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を使って署名認証認証権を実現する--- 論理コード
第一歩:フィルタの作成

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           (  )
 * @WebFilter         URL
 *   URL          ,     @Order(x)     request     ,x        
 * 
 * @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);
			
			/* 
			 *          (     )
			 *  :            ;               ,  @RequestBody        
			 *            getReader() has already been called for this request
			 *     :                     .        ,            ,   .
			 *                     ,             .
			 *              HttpServletRequestWrapper    
			 *       :          、   ;
			 */
			MyRequestWrapper mrw = new MyRequestWrapper(request);
			String bodyString = mrw.getBody();
			logger.info("getted requestbody data is ---> " + bodyString);
			
			//          
			//   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      ,          
			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() {
 
	}
 
}
 
/**
 *     --->           (  )       
 *
 * @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()));
    }
}
第二ステップ:プロジェクトのスタートクラスに@Servlet ComponentScan注釈を追加し、スキャンを許可する
            Servletコンポーネント(フィルタ、モニターなど)。

テストします
テストの説明
    クライアントipは私達が設定したipホワイトリストにあります。 かつタイムスタンプ + secret + bodyStringMD 5暗号化されたフィールドは、要求ヘッダ領域から送られてきたsignature値と同じであり、認証が通過する。
説明:
        1.ipホワイトリスト本例は、サービス端末の対応サービスのシステム構成ファイルaplication.propertiesに設定されている。
        2.secretはクライアント側とサーバー側が決めたMD 5暗号化用の一つです。  セット自体は伝送しません。
        3.bodyStringは、クライアントのrequestを通じてサービス先が取得した要求体のデータである。
        4.signatureはクライアントが暗号化した値であり、サーバーは元のデータをクライアントと同じ暗号化するだけで、
           暗号化結果と転送サービス側のsignatureを比較すると、認証が通過します。
プロジェクトを起動し、postmanを使ってテストしてください。

プログラム印刷のログを与えると、より分かりやすくなります。

ヒント:本人がテストする時、私のパソコンはサーバーでもクライアントでもあるので、そのようなipを取得しました。
注:ipまたはAuthortization値のいずれかまたは二つが条件を満たしていない場合、フロントエンド403に戻ります。
     ここでは効果図は与えられません。
テストの結果から分かりました。署名認証が成功しました。
テスト項目コード委託管理リンク:  https://github.com/JustryDeng/PublicRepository ​
ここで、SpringBootに関するFilterを使って署名認証認証権を実現するための例示的なコードについての記事を紹介します。Spring Boot Filter署名認証の内容については、以前の文章を検索したり、下記の関連記事を引き続き閲覧したりしてください。これからもよろしくお願いします。