在项目中遇到了需要在实现HTTP反向代理的同时作为一个网关进行鉴权。我选择了 Smiley's HTTP Proxy Servlet 来实现。
原始ProxyServlet类
package org.mitre.dsmiley.httpproxy;
import javax.servlet.http.HttpServlet;
@SuppressWarnings({"deprecation", "serial", "WeakerAccess"})
public class ProxyServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
throws ServletException, IOException {
}
}
对这个类进行一些重写
自定义异常处理
override fun service(servletRequest: HttpServletRequest?, servletResponse: HttpServletResponse?) {
logger.info("========= 重写请求 =========")
try {
super.service(servletRequest, servletResponse)
} catch (e: ZZException) {
logger.error("ZZException", e)
handleZZException(e, servletRequest!!, servletResponse!!)
}
}
private fun handleZZException(ex: ZZException, request: HttpServletRequest, response: HttpServletResponse) {
logger.info("RequestURI:{}", request.requestURI)
response.status = HttpStatus.FORBIDDEN.value()
response.contentType = "application/json;charset=UTF-8"
val errorDetails = mapOf(
"code" to ex.code,
"msg" to ex.message
)
val json = jacksonObjectMapper().writeValueAsString(errorDetails)
response.writer.write(json)
}
重写请求的query参数
override fun rewriteQueryStringFromRequest(servletRequest: HttpServletRequest, queryString: String?): String {
if (queryString.isNullOrBlank()) {
return ""
}
logger.debug("请求参数: $queryString")
val queryStrBuffer = StringBuffer(queryString.length)
queryString.split("&".toRegex()).filter { it.isNotBlank() }.forEach {
processQueryParam(it, queryStrBuffer)
}
if (queryStrBuffer.endsWith("&")) {
val removeRange = queryStrBuffer.removeRange(queryStrBuffer.length - 1, queryStrBuffer.length)
return removeRange.toString()
}
return queryStrBuffer.toString()
}
protected open fun processQueryParam(param: String, queryStrBuffer: StringBuffer) {
if (param.lowercase().contains("tk=")) {
val tkStr = param.substring(3)
if (StrUtil.isBlank(tkStr)) {
throw ZZException("403", "令牌不能为空")
}
tkTL.set(tkStr)
return
}
queryStrBuffer.append("$param&")
}
重写请求的url
protected fun superRewriteUrlFromRequest(servletRequest: HttpServletRequest): String =
super.rewriteUrlFromRequest(servletRequest)
override fun rewriteUrlFromRequest(servletRequest: HttpServletRequest): String {
logger.debug("========= 重写请求url =========")
val uri = StringBuilder(500)
uri.append(getTargetUri(servletRequest))
val pathInfo = rewritePathInfoFromRequest(servletRequest)
uri.append(URLUtil.encode(pathInfo))
var queryString = URLUtil.decode(servletRequest.queryString)
var fragment: String? = null
val fragIdx = queryString?.indexOf('#')
if (fragIdx != null) {
if (fragIdx >= 0) {
fragment = queryString.substring(fragIdx + 1)
queryString = queryString.substring(0, fragIdx)
}
}
queryString = rewriteQueryStringFromRequest(servletRequest, queryString)
val ip = getIpAddr(servletRequest)
val (appInfo, layerInfo) = checkAuth(tkTL.get(), ip, layerIdTL.get())
processLogInfo(appInfo, layerInfo)
uri.append('?')
uri.append(queryString)
if (doSendUrlFragment && fragment != null) {
uri.append('#')
uri.append(encodeUriQuery(fragment, false))
}
logger.info("重写后的url: $uri")
return uri.toString()
}
鉴权处理令牌
protected open fun checkAuth(
tk: String,
ip: String,
layerId: String
): Pair<Map<String, Any>, Map<String, Any?>> {
logger.debug("tk:$tk\nlayerId:$layerId")
val tokenMap = redisTool.getMap<String, Map<String, Any?>>("$platformName:platform:app:token")
val appInfo = tokenMap.getMap(tk)
if (appInfo.isEmpty()) {
throw ZZException("403", "令牌无效")
}
checkIp(appInfo.getString("appIpWhitelist"), ip)
val layers: List<Map<String, Any?>> = appInfo.getList("layers")
val layerInfo = layers.find { StrUtil.equals(it.getString("lRealLayerId"), layerId) }
?: throw ZZException("403", "令牌与图层不匹配")
logger.info("图层信息: $layerInfo")
return Pair(appInfo, layerInfo)
}
注入Servlet
@Bean
fun ogcServletRegistrationBean(proxyServlet: OGCServerProxyServlet): ServletRegistrationBean<OGCServerProxyServlet> {
val servletRegistrationBean = ServletRegistrationBean(proxyServlet, "/geoserver/*")
servletRegistrationBean.addInitParameter(ProxyServlet.P_TARGET_URI, "http://127.0.0.1:8080/geowebcache/service")
servletRegistrationBean.addInitParameter(ProxyServlet.P_LOG, "true")
return servletRegistrationBean
}