JSD-7610 SAML单点
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

355 lines
16 KiB

package com.fr.plugin.xxxx.saml;
import com.fr.base.TemplateUtils;
import com.fr.data.NetworkHelper;
import com.fr.decision.authority.data.User;
import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider;
import com.fr.decision.mobile.terminal.TerminalHandler;
import com.fr.decision.webservice.bean.authentication.OriginUrlResponseBean;
import com.fr.decision.webservice.exception.user.UserNotExistException;
import com.fr.decision.webservice.utils.DecisionServiceConstants;
import com.fr.decision.webservice.utils.DecisionStatusService;
import com.fr.decision.webservice.utils.WebServiceUtils;
import com.fr.decision.webservice.v10.login.LoginService;
import com.fr.decision.webservice.v10.login.TokenResource;
import com.fr.decision.webservice.v10.user.UserService;
import com.fr.general.PropertiesUtils;
import com.fr.locale.InterProviderFactory;
import com.fr.log.FineLoggerFactory;
import com.fr.plugin.xxxx.saml.xxxx.saml.SAMLException;
import com.fr.plugin.xxxx.saml.xxxx.saml.SAMLResponseValidator;
import com.fr.plugin.xxxx.saml.xxxx.saml.constant.SsoConstants;
import com.fr.plugin.xxxx.saml.xxxx.saml.impl.SAMLRequestImpl;
import com.fr.plugin.xxxx.saml.xxxx.saml.impl.SAMLResponseValidatorImpl;
import com.fr.plugin.xxxx.saml.schedule.SyncThread;
import com.fr.plugin.transform.FunctionRecorder;
import com.fr.scheduler.QuartzContext;
import com.fr.scheduler.ScheduleJobManager;
import com.fr.stable.StringUtils;
import com.fr.stable.web.Device;
import com.fr.third.v2.org.quartz.CronScheduleBuilder;
import com.fr.third.v2.org.quartz.JobKey;
import com.fr.third.v2.org.quartz.SchedulerException;
import com.fr.third.v2.org.quartz.TriggerBuilder;
import com.fr.web.utils.WebUtils;
import com.fr.third.org.apache.commons.codec.binary.Base64;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @Author fr.open
* @Date 2020/9/10
* @Description
**/
@FunctionRecorder
public class SsoFilter extends AbstractGlobalRequestFilterProvider {
private static String[] notFilter = {
"/decision/file", "/decision/resources",
"/decision/login/config", "/decision/system/info", "/decision/login/slider",
"/decision/remote", "/share", "/mobileTip.html", "/link"
};
@Override
public String filterName() {
return "honor";
}
@Override
public String[] urlPatterns() {
initSchedule();
return new String[]{"/*"};
}
@Override
public void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) {
if (isLogin(req) || isResource(req)) {
filter(req, res, filterChain);
return;
}
String samlResp = req.getParameter("SAMLResponse");
String viewlet = WebUtils.getHTTPRequestParameter(req, "viewlet");
String reportlet = WebUtils.getHTTPRequestParameter(req, "reportlet");
String formlet = WebUtils.getHTTPRequestParameter(req, "formlet");
String let = StringUtils.isNotBlank(viewlet) ? viewlet : StringUtils.isNotBlank(reportlet) ? reportlet : formlet;
//在判断是否是需授权报表
if (StringUtils.isBlank(samlResp) && StringUtils.isNotBlank(let) && !ReportService.getInstance().isAuth(let)) {
filter(req, res, filterChain);
return;
}
if (isMobileDevice(req) && new MobileFilter().doFilter(req, res, filterChain)) {
return;
}
HttpSession session = req.getSession(true);
if (session == null || session.getAttribute(SsoConstants.SESSION_USER_INFO_KEY) == null) {
FineLoggerFactory.getLogger().info("samlResp is {}", samlResp);
if (samlResp != null && !samlResp.equals("")) {
try {
byte[] buffer = new Base64().decode(samlResp.getBytes(req.getCharacterEncoding() == null ? "UTF-8" : req.getCharacterEncoding()));
FineLoggerFactory.getLogger().info("decode base64 result is {}", new String(buffer, StandardCharsets.UTF_8));
SAMLResponseValidator vld = new SAMLResponseValidatorImpl(
new String(buffer, StandardCharsets.UTF_8));
vld.validate();
//UserInfoBean uiBean = vld.getUIBean();
FineLoggerFactory.getLogger().info("decode user bean is [{}]", vld.getUid());
loginFromToken(req, res, vld.getUid());
FineLoggerFactory.getLogger().info("login bean set session!");
session.setAttribute(SsoConstants.SESSION_USER_INFO_KEY, vld.getUid());
String prefix = TemplateUtils.render("${fineServletURL}");
if (isMobileDevice(req) && (req.getRequestURI().endsWith(prefix + "/") || req.getRequestURI().endsWith(prefix))) {
String url = req.getRequestURL().toString().endsWith("/") ? req.getRequestURL().toString() + "url/mobile" : req.getRequestURL().toString() + "/url/mobile";
FineLoggerFactory.getLogger().info("forward {} to {}", req.getRequestURI(), url);
String jump = getOrigin(req);
FineLoggerFactory.getLogger().info("get Origin path is {}", jump);
if (StringUtils.isNotBlank(jump)) {
res.sendRedirect(jump);
} else {
res.sendRedirect(url);
}
return;
}
String jump = getOrigin(req);
FineLoggerFactory.getLogger().info("get Origin path is {}", jump);
if (StringUtils.isNotBlank(jump)) {
res.sendRedirect(jump);
return;
}
filter(req, res, filterChain);
return;
} catch (SAMLException | UnsupportedEncodingException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
req.setAttribute("SAMLRequest", e.getMessage());
setResError(res, e);
} catch (UserNotExistException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
setResError(res, e);
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
} else {
try {
FineLoggerFactory.getLogger().info("samlResp is null,redirect login!");
/*byte[] samlRequest = new SAMLRequestImpl().generate().getBytes(StandardCharsets.UTF_8);
samlRequest = new Base64().encode(samlRequest);
req.setAttribute("SAMLRequest", new String(samlRequest, StandardCharsets.UTF_8));
FineLoggerFactory.getLogger().info("return page is:{}",PropertiesUtils.getProperties("conf").getProperty("return"));
req.getRequestDispatcher(PropertiesUtils.getProperties("conf").getProperty("return")).forward(req, res);*/
byte[] samlRequest = new SAMLRequestImpl().generate().getBytes(StandardCharsets.UTF_8);
String saml = new String(new Base64().encode(samlRequest), StandardCharsets.UTF_8.name());
String url = PropertiesUtils.getProperties("conf").getProperty("return");
String back = String.format("%s?SAMLRequest=%s&RelayState=null", url, URLEncoder.encode(saml, StandardCharsets.UTF_8.toString()));
FineLoggerFactory.getLogger().info("back login :{}", back);
String jump = req.getRequestURL() + (req.getQueryString() == null ? StringUtils.EMPTY : "?" + req.getQueryString());
String id = UUID.randomUUID().toString();
DecisionStatusService.originUrlStatusService().put(id, new OriginUrlResponseBean(jump));//添加重定向地址
Cookie cookie = new Cookie("ORIGIN_URL", id);
cookie.setPath("/");
cookie.setHttpOnly(true);
res.addCookie(cookie);
res.sendRedirect(back);
return;
} catch (SAMLException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
req.setAttribute("SAMLRequest", e.getMessage());
setResError(res, e);
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
}
}
String id = (String) session.getAttribute(SsoConstants.SESSION_USER_INFO_KEY);
if (StringUtils.isNotBlank(id)) {
FineLoggerFactory.getLogger().info("get user:{} form session", id);
loginFromToken(req, res, id);
filter(req, res, filterChain);
}
return;
}
private void setError(HttpServletResponse res) {
try {
PrintWriter printWriter = WebUtils.createPrintWriter(res);
Map<String, Object> map = new HashMap<>();
map.put("result", InterProviderFactory.getProvider().getLocText("Fine-Engine_Error_Page_Result"));
map.put("reason", "权限生效时间为2小时,如有疑问,请联系运维人员");
map.put("solution", InterProviderFactory.getProvider().getLocText("Fine-Engine_Please_Contact_Platform_Admin"));
String page = WebServiceUtils.parseWebPageResourceSafe("com/fr/web/controller/decision/entrance/resources/unavailable.html", map);
printWriter.write(page);
printWriter.flush();
printWriter.close();
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
}
private String getViewlet(HttpServletRequest req) {
return WebUtils.getHTTPRequestParameter(req, "viewlet") == null ?
WebUtils.getHTTPRequestParameter(req, "formlet")
: WebUtils.getHTTPRequestParameter(req, "viewlet");
}
private String getOrigin(HttpServletRequest req) throws Exception {
Cookie[] cookies = req.getCookies();
if (cookies == null) {
return null;
}
for (int i = 0; i < cookies.length; i++) {
if ("ORIGIN_URL".equals(cookies[i].getName())) {
String id = cookies[i].getValue();
OriginUrlResponseBean path = DecisionStatusService.originUrlStatusService().get(id);
return path == null ? StringUtils.EMPTY : path.getOriginUrl();
}
}
return StringUtils.EMPTY;
}
private boolean isResource(HttpServletRequest req) {
String viewlet = WebUtils.getHTTPRequestParameter(req, "viewlet");
String reportlet = WebUtils.getHTTPRequestParameter(req, "reportlet");
String formlet = WebUtils.getHTTPRequestParameter(req, "formlet");
String let = StringUtils.isNotBlank(viewlet) ? viewlet : StringUtils.isNotBlank(reportlet) ? reportlet : formlet;
String op = WebUtils.getHTTPRequestParameter(req, "op");
if (StringUtils.isNotBlank(op) && StringUtils.isBlank(let)) {
return true;
}
if (req.getRequestURI().endsWith("js") || req.getRequestURI().endsWith("css")) {
return true;
}
for (int i = 0; i < notFilter.length; i++) {
if (req.getRequestURI().contains(notFilter[i])) {
return true;
}
}
return false;
}
private void setResError(HttpServletResponse res, Exception e) {
try {
WebUtils.printAsString(res, e.getMessage());
} catch (Exception exception) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
}
private boolean loginFromToken(HttpServletRequest req, HttpServletResponse res, String username) {
try {
if (StringUtils.isNotEmpty(username)) {
FineLoggerFactory.getLogger().info("current username:" + username);
User user = UserService.getInstance().getUserByUserName(username);
FineLoggerFactory.getLogger().info("get user:" + user);
if (user == null) {
throw new UserNotExistException();
}
String token = LoginService.getInstance().login(req, res, username);
FineLoggerFactory.getLogger().info("get login token:" + token);
req.setAttribute(DecisionServiceConstants.FINE_AUTH_TOKEN_NAME, token);
FineLoggerFactory.getLogger().info("username:" + username + "login success");
return true;
} else {
FineLoggerFactory.getLogger().warn("username is null!");
return false;
}
} catch (UserNotExistException e) {
throw new UserNotExistException();
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
return false;
}
private void filter(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) {
try {
filterChain.doFilter(req, res);
} catch (IOException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
} catch (ServletException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
}
private boolean isLogin(HttpServletRequest request) {
String oldToken = TokenResource.COOKIE.getToken(request);
return oldToken != null && checkTokenValid(request, (String) oldToken);
}
private boolean checkTokenValid(HttpServletRequest req, String token) {
try {
Device device = NetworkHelper.getDevice(req);
LoginService.getInstance().loginStatusValid(token, TerminalHandler.getTerminal(req, device));
return true;
} catch (Exception ignore) {
}
return false;
}
public boolean isMobileDevice(HttpServletRequest request) {
String deviceType = WebUtils.getHTTPRequestParameter(request,"deviceType");
if(StringUtils.isNotBlank(deviceType)){
FineLoggerFactory.getLogger().info("get deviceType is {}",deviceType);
return StringUtils.equals(deviceType,"mobile");
}
String requestHeader = request.getHeader("user-agent");
String[] deviceArray = new String[]{"android", "iphone", "ios", "windows phone"};
if (requestHeader == null) {
return false;
}
requestHeader = requestHeader.toLowerCase();
for (int i = 0; i < deviceArray.length; i++) {
if (requestHeader.contains(deviceArray[i])) {
FineLoggerFactory.getLogger().info("current request:{} is mobile request!", request.getRequestURI());
return true;
}
}
return false;
}
public static void initSchedule() {
JobKey jobKey = new JobKey("syncRole", "syncGroup");
try {
if (QuartzContext.getInstance().getScheduler().checkExists(jobKey)) {
ScheduleJobManager.getInstance().removeJob(jobKey.getName(), jobKey.getGroup());
}
} catch (SchedulerException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
addSchedule(jobKey);
}
private static void addSchedule(JobKey jobKey) {
Map<String, Object> param = new HashMap();
TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger();
triggerBuilder.forJob(jobKey.getName(), jobKey.getGroup()).withIdentity(jobKey.getName(), jobKey.getGroup()).startNow();
String cron = com.fr.base.PropertiesUtils.getProperties("conf").getProperty("cron");
if (StringUtils.isBlank(cron)) {
FineLoggerFactory.getLogger().error("cron is null schedule start failed");
return;
}
CronScheduleBuilder schedule = CronScheduleBuilder.cronSchedule(cron);
triggerBuilder.withSchedule(schedule);
try {
ScheduleJobManager.getInstance().addJob(jobKey.getName(), jobKey.getGroup(), "sync job", SyncThread.class, triggerBuilder.build(), param);
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
}
}