JSD-7565 第三方token交换fine_auth_token
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.

538 lines
22 KiB

package com.fr.plugin.nfsq.sso;
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.LoginRequestInfoBean;
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.v10.login.LoginService;
import com.fr.decision.webservice.v10.login.TokenResource;
import com.fr.decision.webservice.v10.user.UserService;
import com.fr.general.http.HttpRequest;
import com.fr.general.http.HttpToolbox;
import com.fr.io.utils.ResourceIOUtils;
import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory;
import com.fr.plugin.transform.FunctionRecorder;
import com.fr.stable.StringUtils;
import com.fr.stable.web.Device;
import com.fr.web.utils.WebUtils;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.time.Instant;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @Author fr.open
* @Date 2020/9/10
* @Description
**/
@FunctionRecorder
public class SsoFilter extends AbstractGlobalRequestFilterProvider {
private final String CODE_KEY = "90bb6046";
@Override
public String filterName() {
return "nongfu";
}
@Override
public String[] urlPatterns() {
return new String[]{"/*"};
}
private static final String[] NOT_FILTER = {
"/decision/file",
"/decision/resources",
"/system",
"/materials.min.js.map",
"/remote",
"/login",
"/login/config",
"/getFineToken"
};
private String apiAuthorize;
private String apiAuthorizeResponseType;
private String apiClientId;
private String apiGetUser;
private String apiRefreshToken;
private String state;
public SsoFilter() {
InputStream in = ResourceIOUtils.read("/resources/xplatform.properties");
Properties properties = new Properties();
try {
properties.load(in);
} catch (IOException e) {
FineLoggerFactory.getLogger().error(e.getMessage(),e);
}
this.apiAuthorize = properties.getProperty("api.authorize");
this.apiClientId = properties.getProperty("api.authorize.client_id");
this.apiAuthorizeResponseType = properties.getProperty("api.authorize.response_type");
this.apiGetUser = properties.getProperty("api.get-user");
this.apiRefreshToken = properties.getProperty("api.refresh-token");
this.state = properties.getProperty("api.authorize.state");
}
@Override
public void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) {
String thisUrl = req.getRequestURL().toString();
FineLoggerFactory.getLogger().info("This request [{}] login status is [{}]", thisUrl, isLogin(req));
if (req.getRequestURI().endsWith("auth")) {
executeGetAuth(req, res);
return;
}
String code = req.getParameter("sign");
if (StringUtils.isNotBlank(code)) {
loginFromCode(code, req, res);
filter(req, res, filterChain);
return;
}
if (Stream.of(NOT_FILTER).anyMatch(thisUrl::contains) || isLogin(req) || isMobileDevice(req)) {
filter(req, res, filterChain);
return;
}
String prefix = null;
try {
prefix = TemplateUtils.render("${fineServletURL}");
if (req.getRequestURI().endsWith(prefix + "/login")) {
if (handlerWeChat(req, res)) {
return;
}
filter(req, res, filterChain);
return;
}
}catch (IllegalAccessException e){
redirectAuth(req, res);
return;
}catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
Params params = null;
try {
params = getUsername(req);
} catch (IllegalAccessException e) {
redirectAuth(req, res);
return;
}
FineLoggerFactory.getLogger().info("Getted username is [{}]", params.getUsername());
if (StringUtils.isNotBlank(params.getUsername())) {
Cookie at = new Cookie("access_token", params.getAccessToken());
at.setDomain("xxxx");
at.setMaxAge(7200);
Cookie rt = new Cookie("refresh_token", params.getRefreshToken());
rt.setDomain("xxxx");
rt.setMaxAge(7200);
res.addCookie(at);
res.addCookie(rt);
loginFromToken(req, res, params.getUsername());
try {
res.sendRedirect(getRedirectUriWithCachedParams(req));
} catch (IOException e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
return;
}
redirectAuth(req, res);
}
private void redirectAuth(HttpServletRequest req, HttpServletResponse res) {
try {
res.sendRedirect(getAuthorizeUrl(req,res));
} catch (IOException e) {
FineLoggerFactory.getLogger().error(e.getMessage(),e);
}
}
private boolean handlerWeChat(HttpServletRequest request, HttpServletResponse res) throws IOException, IllegalAccessException {
// 有帆软的登录信息
if (isLogin(request)) {
FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> 已登录");
String token = getAccessToken(request);
FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> token value is [{}]", token);
// 没有获取到access_token
if (StringUtils.isBlank(token)) {
FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> token value is empty, 不做处理");
return false;
}
// 获取到了access_token, 比对帆软已登录的用户名和access_token对应的用户名
String username = getUsername(request, token).getUsername();
FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> token 对应的用户名[{}]", username);
String username1 = LoginService.getInstance().getUserNameFromRequestCookie(request);
FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> 已登录的用户名[{}]", username1);
if (Objects.equals(username1, username)) {
FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> 两个用户名相同, 不做处理");
return false;
}
// 不一样的话使用access_token对应的用户名重新登录
FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> 两个用户名不相同, 使用[{}]重新登录", username);
loginFromToken(request, res, username);
return false;
}
// 没有登录信息
FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> 未登录");
String token = getAccessToken(request);
FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> token value is [{}]", token);
if (StringUtils.isNotBlank(token)) {
FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> token value is not empty, 处理自动登录逻辑");
Params params = getUsername(request, token);
FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> 获取到的用户名[{}]", params.getUsername());
if (StringUtils.isNotBlank(params.getUsername()) && exist(params.getUsername())) {
Cookie at = new Cookie("access_token", params.getAccessToken());
at.setDomain("yst.com.cn");
Cookie rt = new Cookie("refresh_token", params.getRefreshToken());
rt.setDomain("yst.com.cn");
loginFromToken(request, res, params.getUsername());
res.sendRedirect(getRedirectUriWithCachedParams(request));
return true;
}
}
FineLoggerFactory.getLogger().info("/decision请求特殊处理 >>> token value is empty, 跳转到sso登录页");
res.sendRedirect(getAuthorizeUrl(request, res));
return true;
}
private String getAuthorizeUrl(HttpServletRequest request, HttpServletResponse res) throws UnsupportedEncodingException {
String urlPattern = "%s?response_type=%s&client_id=%s&redirect_uri=%s&_=%s&state=%s";
String state = cacheParams(request);
res.addCookie(new Cookie("FINE_REDIRECT_PARAM",state));
if (StringUtils.isNotBlank(state)) {
urlPattern += "&target=" + state;
}
String url = String.format(urlPattern,
apiAuthorize,
apiAuthorizeResponseType,
apiClientId,
URLEncoder.encode(request.getRequestURL().toString(), "utf-8"),
Instant.now().toEpochMilli(),
this.state
);
FineLoggerFactory.getLogger().info("授权登录页面[{}]", url);
return url;
}
private String cacheParams(HttpServletRequest request) {
Enumeration<String> names = request.getParameterNames();
Map<String, String> value = new HashMap<>();
while (names.hasMoreElements()) {
String name = names.nextElement();
value.put(name, request.getParameter(name));
}
if (value.isEmpty()) {
return StringUtils.EMPTY;
}
String key = UUID.randomUUID().toString();
try {
DecisionStatusService.originUrlStatusService().put(key, value);
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
return key;
}
/**
* 登录成功后将缓存中存储的url参数拼接入对应地址
*
* @param request
* @return
*/
private String getRedirectUriWithCachedParams(HttpServletRequest request) {
String redirectUri = request.getRequestURL().toString();
String key = request.getParameter("target");
if(StringUtils.isBlank(key)){
key = getCookieValue(request, "FINE_REDIRECT_PARAM");
}
if(StringUtils.isBlank(key)){
return redirectUri;
}
Map<String, String> value = new HashMap<>();
try {
value = DecisionStatusService.originUrlStatusService().get(key);
if (com.fr.stable.StringUtils.isNotBlank(key) && !value.isEmpty()) {
DecisionStatusService.originUrlStatusService().delete(key);
Map<String, String> finalValue = value;
redirectUri += "?" + value.keySet().stream().map(k -> String.format("%s=%s", k, finalValue.get(k))).collect(Collectors.joining("&"));
}
} catch (Exception e) {
e.printStackTrace();
}
return redirectUri;
}
private boolean exist(String username) {
User user = null;
try {
user = UserService.getInstance().getUserByUserName(username);
} catch (Exception e) {
e.printStackTrace();
}
return user != null;
}
private Params getUsername(HttpServletRequest request) throws IllegalAccessException {
String token = getAccessToken(request);
if(StringUtils.isBlank(token)){
FineLoggerFactory.getLogger().info("token is null");
return new Params();
}
return getUsername(request, token);
}
private Params getUsername(HttpServletRequest request, String accessToken) throws IllegalAccessException {
String url = apiGetUser + "?access_token=" + accessToken;
FineLoggerFactory.getLogger().info("Get user api address is [{}]", url);
try {
String res = HttpToolbox.executeAndParse(HttpRequest.custom().url(url)
.get()
.build());
FineLoggerFactory.getLogger().info("获取用户信息接口返回内容 ==> {}", res);
JSONObject body = new JSONObject(res);
if (body.getBoolean("success") && body.has("data")) {
body = body.getJSONObject("data");
if (body.has("account")) {
Params params = new Params();
params.setRefreshToken(getRefreshToken(request));
params.setUsername(body.getString("account"));
params.setAccessToken(accessToken);
return params;
}
}
if (body.has("message") && "token不合法".equals(body.getString("message"))) {
FineLoggerFactory.getLogger().info("Access Token [{}] 不合法, 使用Refresh Token[{}]重新获取", accessToken, getRefreshToken(request));
accessToken = getAccessTokenWithRefreshToken(request);
return getUsername(request, accessToken);
}
throw new IllegalAccessException();
}catch (IllegalAccessException e){
throw new IllegalAccessException();
}catch (Exception e) {
FineLoggerFactory.getLogger().error("获取用户名失败", e);
throw new RuntimeException(e);
}
}
private String getAccessTokenWithRefreshToken(HttpServletRequest request) throws Exception {
String url = apiRefreshToken + String.format("?refresh_token=%s&client_id=%s&grant_type=refresh_token", getRefreshToken(request), apiClientId);
FineLoggerFactory.getLogger().info("Refresh Token api address is [{}]", url);
String res = HttpToolbox.executeAndParse(HttpRequest.custom().url(url)
.get()
.build());
FineLoggerFactory.getLogger().info("刷新token接口返回内容 ==> {}", res);
JSONObject body = new JSONObject(res);
if (body.getBoolean("success") && body.has("data")) {
body = body.getJSONObject("data");
if (body.has("access_token")) {
return body.getString("access_token");
}
}
throw new IllegalAccessException();
}
private String getRefreshToken(HttpServletRequest request) {
return getValueFromRequest(request, "refresh_token");
}
private String getAccessToken(HttpServletRequest request) {
return getValueFromRequest(request, "access_token");
}
private String getValueFromRequest(HttpServletRequest request, String key) {
try {
String value = request.getParameter(key);
if (StringUtils.isEmpty(value)) {
value = getCookieValue(request, key);
}
if (StringUtils.isEmpty(value)) {
String params = request.getParameter("app");
params = params.substring(params.indexOf("#") + 1);
Map<String, String> values = Pattern.compile("&").splitAsStream(params).map(e -> e.split("=")).collect(Collectors.toMap(e -> e[0], e -> e[1]));
value = values.get(key);
}
return value;
} catch (Exception e) {
FineLoggerFactory.getLogger().error("Failed to get \"{}\" value from this request, cause by: ", key, e.getMessage());
FineLoggerFactory.getLogger().error("", e);
}
return "";
}
private String getCookieValue(HttpServletRequest request, String key) {
Cookie[] cookies = request.getCookies();
for (Cookie c : cookies) {
if (StringUtils.equals(c.getName(), key)) {
return c.getValue();
}
}
return StringUtils.EMPTY;
}
private void loginFromCode(String code, HttpServletRequest req, HttpServletResponse res) {
FineLoggerFactory.getLogger().info("ssoFilter >>> inside login code param is {}", code);
try {
code = DesECBUtil.decryptDES(code, CODE_KEY);
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
FineLoggerFactory.getLogger().info("ssoFilter >>> inside login code decode is {}", code);
String[] arr = code.split("_");
loginFromToken(req, res, arr[0]);
}
private void executeGetAuth(HttpServletRequest req, HttpServletResponse res) {
String uri = WebUtils.getHTTPRequestParameter(req, "redirect_uri");
String currentUsername = null;
try {
User user= null;
try {
user = UserService.getInstance().getUserByRequest(req);
}catch (Exception e){
}
if(user == null){
user = UserService.getInstance().getUserByRequestCookie(req);
}
currentUsername = user.getUserName();
String encryptDES = DesECBUtil.encryptDES(String.format("%s_%d", currentUsername, Instant.now().toEpochMilli()), CODE_KEY);
encryptDES = URLEncoder.encode(encryptDES, "UTF-8");
uri = uri.indexOf("?") == -1 ? uri + "?sign=" + encryptDES : uri + "&sign=" + encryptDES;
res.sendRedirect(uri);
FineLoggerFactory.getLogger().info("ssoFilter >>> inside redirect url is {}", uri);
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
}
private boolean notExistUser(String username) {
User user = null;
try {
user = UserService.getInstance().getUserByUserName(username);
if (user == null) {
return true;
}
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
return false;
}
private boolean hasCookie(HttpServletRequest req) {
if (req.getCookies() == null) {
return false;
}
return Stream.of(req.getCookies()).anyMatch(e -> "IAM_SESSION".equalsIgnoreCase(e.getName()));
}
public LoginRequestInfoBean getLoginInfo(HttpServletRequest req) {
try {
BufferedReader br = req.getReader();
String str = "";
String listString = "";
while ((str = br.readLine()) != null) {
listString += str;
}
JSONObject jsonObject = new JSONObject(listString);
LoginRequestInfoBean info = jsonObject.mapTo(LoginRequestInfoBean.class);
//info.setPassword(TransmissionTool.decrypt(info.isEncrypted(),info.getPassword()));
return info;
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}
return null;
}
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 (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 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]) && request.getRequestURI().endsWith("/login")) {
FineLoggerFactory.getLogger().info("current request:{} is mobile request!", request.getRequestURI());
return true;
}
}
return false;
}
}