文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

SpringBoot使用Filter实现签名认证鉴权的示例代码

2024-04-02 19:55

关注

情景说明

        鉴权,有很多方案,如:SpringSecurity、Shiro、拦截器、过滤器等等。如果只是对一些URL进行认证鉴权的话,我们完
全没必要引入SpringSecurity或Shiro等框架,使用拦截器或过滤器就足以实现需求。
        本文介绍如何使用过滤器Filter实现URL签名认证鉴权。

本人测试软硬件环境:Windows10、Eclipse、SpringBoot、JDK1.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>

第二步:在系统配置文件application.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

第三步:准备获取客户端IP的工具类


import java.net.InetAddress;
import java.net.UnknownHostException;
 
import javax.servlet.http.HttpServletRequest;
 

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;
    }
}

第四步:准备MD5加密工具类


import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Hex;
 

public class MDUtils {
 
	
	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));
	}
 
	
	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;
 

@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);
			
			
			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() {
 
	}
 
}
 

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() {
            
            @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组件(过滤器、监听器等)。

测试一下

测试说明

    客户端ip在我们设置的ip白名单里面 且 timestamp + secret + bodyStringMD5加密后的字段与请求头域中传过来的signature值相同时,才算鉴权通过。

说明:

        1.ip白名单 本示例中是设置在服务端的相应服务的系统配置文件application.properties中的。
        2.secret 是客户端一方和服务端一方 定好的一个用来MD5加密的   数,secret本身不进行传输。
        3.bodyString是服务端通过客户端的request获取到的请求体中的数据。
        4.signature是客户端加密后的值,服务端只需对原始数据进行和客户端进一模一样的加密,
           将加密结果和传导服务端的signature进行比对,一样则鉴权通过。

启动项目,使用postman测试一下

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

提示:由于本人测试时,我的电脑既是服务器又是客户端,所以获取到了那样的ip。

注:当ip或Authorization值中任意一个或两个 不满足条件时,会返回给前端403(见:SignAutheFilter中的相关代码),
     这里就不给出效果图了。

由测试结果可知:签名鉴权成功!

测试项目代码托管链接: https://github.com/JustryDeng/PublicRepository ​

到此这篇关于SpringBoot使用Filter实现签名认证鉴权的示例代码的文章就介绍到这了,更多相关SpringBoot Filter签名认证鉴权内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯