1、过滤器实现
package com.zrar.arask.cs.filter;
import com.zrar.arask.cs.util.JWTUtil;
import com.zrar.easyweb.core.util.StringUtil;
import com.zrar.easyweb.jose.jwt.SignedJWT;
import com.zrar.easyweb.util.GsonUtil;
import com.zrar.easyweb.web.core.bean.JsonResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 消息服务令牌校验拦截器
*/
public class MessageServiceFilter implements Filter{
private Logger logger = LoggerFactory.getLogger(MessageServiceFilter.class);
// 服务Urls
private String[] serviceUrls = {};
// 是否启用时间限制
private boolean enableTimeLimit = false;
// 时效长(默认15分钟)
public static final AtomicLong TIME_LIMIT_APP_SERVICE = new AtomicLong(15 * 60 * 1000);
// 无效密钥
public static final int CODE_TOKEN_INVALID = 40001;
// 密钥格式错误
public static final int CODE_TOKEN_FORMAT_ERROR = 40002;
// 缺失token信息
public static final int CODE_TOKEN_MISSING = 41001;
// token超时
public static final int CODE_TOKEN_TIMEOUT = 42001;
@Override
public void init(FilterConfig config) throws ServletException {
String urls = StringUtil.null2Str(config.getInitParameter("serviceUrls"));
// 参数不为空,初始化服务路径信息
if (urls.length() > 0) {
serviceUrls = urls.split(",");
}
// 是否启用token时限
String enableTimeLimit = StringUtil.null2Str(config.getInitParameter("enableTimeLimit"));
this.enableTimeLimit = enableTimeLimit.equals("true") || enableTimeLimit.equals("1");
if (logger.isInfoEnabled()) {
logger.info("Service Token 时效控制 {}", this.enableTimeLimit ? "启用" : "未启用");
}
// 时效长
String timeLimit = StringUtil.null2Str(config.getInitParameter("timeLimit"));
if (StringUtil.isNotNull(timeLimit)) {
if (StringUtil.isNumeric(timeLimit)) {
int min = Integer.parseInt(timeLimit);
TIME_LIMIT_APP_SERVICE.set(min * 60 * 1000);
if (logger.isInfoEnabled()) {
logger.info("Service Token 时效长度 {} 分钟", TIME_LIMIT_APP_SERVICE.get() / 60 / 1000);
}
} else {
logger.error("web.xml > MessageServiceFilter [ timeLimit ] 参数必须为数字");
}
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 请求地址
HttpServletRequest req = ((HttpServletRequest) request);
String contextPath = req.getSession().getServletContext().getContextPath();
String visitURL = req.getRequestURI();
// 验证该地址是否需要验证
boolean flag = this.isValidateUrl(visitURL, contextPath);
if (flag == true) {
//白名单
chain.doFilter(request, response);
return;
}
// 令牌信息
String stoken = StringUtil.null2Str(request.getParameter("stoken"));
// 没有令牌信息,拒绝服务
if (StringUtil.isNull(stoken)) {
this.forbidden(response, CODE_TOKEN_MISSING, "没有发现令牌");
return;
}
// 默认令牌验证不通过
boolean result = false;
SignedJWT jwt = null;
try {
// 验证令牌有效性
jwt = JWTUtil.verifyToken(stoken);
result = jwt.verify(JWTUtil.getJWTVerifier(JWTUtil.getJWTSecret()));
// 如果启用了时限,检查时限
if (result == true && enableTimeLimit == true) {
Date expirationTime = jwt.getBaseClaimsSet().getExpirationTime();
Date now = new Date();
boolean isInTime = now.before(expirationTime);
// 已经超过时限
if (isInTime == false) {
if (logger.isInfoEnabled()) {
logger.info("Service Token 已失效");
}
forbidden(response, CODE_TOKEN_TIMEOUT, "令牌已失效");
return;
}
}
if (logger.isInfoEnabled()) {
logger.info("Service Token Check :[{}] > {}", result, stoken);
}
} catch (Exception e) {
if (logger.isInfoEnabled()) {
logger.info("Service Token Check :[格式不正确] > {}", stoken);
}
forbidden(response, CODE_TOKEN_FORMAT_ERROR,"令牌格式不正确");
return;
}
// 验证通过,继续服务
if (result == true) {
// 设置服务检查通过变量,便于在session验证时通过该请求
request.setAttribute("app_service_auth_result", 1);
chain.doFilter(request, response);
} else {
// 验证不通过,拒绝服务
forbidden(response, CODE_TOKEN_INVALID, "无效令牌");
return;
}
}
@Override
public void destroy() {
}
/**
* 拒绝服务
*
* @param response
* @throws IOException
*/
private void forbidden(ServletResponse response,int code, String message) throws IOException {
JsonResult result =new JsonResult();
result.setMsg(message);
result.setCode(code);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
PrintWriter out = response.getWriter();
out.write(GsonUtil.bean2json(result));
out.flush();
out.close();
}
/**
* 是否属于验证的URL地址
*
* @param currentRequestURL
* @param
* @param
* @return
*/
private boolean isValidateUrl(String currentRequestURL,String contextPath) {
for (String url : this.serviceUrls) {
Pattern p = Pattern.compile(contextPath + url);
Matcher matcher = p.matcher(currentRequestURL);
boolean matches = matcher.matches();
if (matches) {
return true;
}
}
return false;
}
}
2、过滤器配置,xml
<!-- MESSAGE QUERY -->
<filter>
<filter-name>MessageServiceFilter</filter-name>
<filter-class>com.zrar.arask.cs.filter.MessageServiceFilter</filter-class>
<init-param>
<param-name>serviceUrls</param-name>
<param-value>
/custom/config,/custom/checkState,/custom/idleGroup,/custom/groupInfos,/message/sendMessage
</param-value>
</init-param>
<init-param>
<param-name>enableTimeLimit</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>timeLimit</param-name>
<param-value>1</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>MessageServiceFilter</filter-name>
<url-pattern>/message/*</url-pattern>
</filter-mapping>