JSD-8420 开源任务材料 OAuth2单点
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.

203 lines
7.9 KiB

package com.fr.plugin.sunac.sso;
import com.fr.base.Base64;
import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider;
import com.fr.decision.webservice.bean.user.UserBean;
import com.fr.decision.webservice.v10.user.UserService;
import com.fr.general.PropertiesUtils;
import com.fr.general.http.HttpToolbox;
import com.fr.intelli.record.Focus;
import com.fr.intelli.record.Original;
import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory;
import com.fr.plugin.context.PluginContexts;
import com.fr.plugin.transform.FunctionRecorder;
import com.fr.security.encryption.transmission.TransmissionEncryptors;
import com.fr.stable.StringUtils;
import com.fr.stable.fun.Authorize;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Stream;
import static com.fr.plugin.sunac.sso.CommonUtils.*;
/**
* @author fr.open
* @since 2021/8/10
*/
@FunctionRecorder
@Authorize(callSignKey = PluginConstants.PLUGIN_ID)
public class SsoFilter extends AbstractGlobalRequestFilterProvider {
private static String[] NOT_FILTER = {
"/decision/file",
"/decision/resources",
"/system",
"/materials.min.js.map",
"/remote",
"/login",
"/url/mobile",
"/pushResult",
"/login/config",
"/css/"
};
private final String apiAuthorize;
private final String apiClientId;
private final String apiRedirectURI;
private final String apiGetToken;
private final String apiClientSecret;
private final String apiGetUser;
private final String defaultPassword;
public SsoFilter() {
Properties props = PropertiesUtils.getProperties("sunac");
apiAuthorize = getProperty(props, "api.authorize", true);
apiClientId = getProperty(props, "api.client_id", true);
apiRedirectURI = getProperty(props, "api.redirect_uri", true);
apiGetToken = getProperty(props, "api.get-token", true);
apiClientSecret = getProperty(props, "api.client_secret", true);
apiGetUser = getProperty(props, "api.get-user", true);
defaultPassword = getProperty(props, "new.user.password", "MfMy8c96Aqyqzt6F", true);
}
@Override
@Focus(id = PluginConstants.PLUGIN_ID, text = "融创西南单点登录", source = Original.PLUGIN)
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
if (!PluginContexts.currentContext().isAvailable() || isAccept(request)) {
next(request, response, chain);
return;
}
try {
if (StringUtils.isBlank(apiAuthorize)
|| StringUtils.isBlank(apiClientId)
|| StringUtils.isBlank(apiRedirectURI)
|| StringUtils.isBlank(apiClientSecret)
|| StringUtils.isBlank(apiGetToken)
|| StringUtils.isBlank(apiGetUser)) {
throw new RuntimeException("Property [api.authorize] " +
"or [api.client_id] " +
"or [api.redirect_uri] " +
"or [api.client_secret]" +
"or [api.get-token]" +
"or [api.get-user]" +
"hasn't a valid value");
}
String code = request.getParameter("code");
if (StringUtils.isBlank(code)) {
jumpAuthorize(request, response);
return;
}
String username = getUsername(getToken(code));
try {
getUser(username);
} catch (Exception e) {
UserBean bean = new UserBean();
bean.setEnable(true);
bean.setRealName(username);
bean.setUsername(username.toLowerCase());
bean.setPassword(TransmissionEncryptors.getInstance().encrypt(defaultPassword));
UserService.getInstance().addUser(bean);
}
login(username, request, response);
String state = request.getParameter("state");
if (StringUtils.isNotBlank(state)) {
String accessURL = getCachedParam(state, "accessURL");
if (StringUtils.isNotBlank(accessURL)) {
response.sendRedirect(accessURL);
return;
}
}
next(request, response, chain);
} catch (Exception e) {
FineLoggerFactory.getLogger().error("SSO processing failed, Cause by: ", e);
setError(response, e.getMessage());
}
}
private String getUsername(String token) throws IOException {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", token);
headers.put("Content-Type", "application/x-www-form-urlencoded");
String res = HttpToolbox.get(apiGetUser, Collections.emptyMap(), headers);
FineLoggerFactory.getLogger().info("获取用户信息接口返回内容: \"{}\"", res);
JSONObject body = new JSONObject(res);
if (body.has("uid")) {
String username = body.getString("uid");
FineLoggerFactory.getLogger().info("获取到的用户名为: \"{}\"", username);
return username;
}
throw new RuntimeException("用户信息获取失败, 详见接口返回内容");
}
private void jumpAuthorize(HttpServletRequest request, HttpServletResponse response) throws IOException {
String state = UUID.randomUUID().toString();
String accessURL = request.getRequestURI();
if (StringUtils.isNotBlank(request.getQueryString())) {
accessURL += "?" + request.getQueryString();
}
Map<String, String> params = new HashMap<>();
params.put("accessURL", accessURL);
cacheParams(state, params);
String pattern = "%s?client_id=%s&redirect_uri=%s&response_type=code&scope=UserProfile.me&state=%s";
pattern = String.format(pattern, apiAuthorize, apiClientId, apiRedirectURI, state);
response.sendRedirect(pattern);
}
private String getToken(String code) throws IOException {
Map<String, Object> params = new HashMap<>();
params.put("redirect_uri", apiRedirectURI);
params.put("code", code);
params.put("grant_type", "authorization_code");
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/x-www-form-urlencoded");
headers.put("Authorization", "Basic " + Base64.encode((apiClientId + ":" + apiClientSecret).getBytes(StandardCharsets.UTF_8)));
String res = HttpToolbox.post(apiGetToken, params, headers);
FineLoggerFactory.getLogger().info("获取Token接口返回内容: \"{}\"", res);
JSONObject body = new JSONObject(res);
if (body.has("access_token")) {
String token = body.getString("access_token");
FineLoggerFactory.getLogger().info("获取到的token值为: \"{}\"", token);
return token;
}
throw new RuntimeException("获取token失败, 详见接口返回内容");
}
public boolean isAccept(HttpServletRequest request) {
// 已登录
if (isLogin(request)) {
return true;
}
// 手机端请求
if (isMobileDevice(request)) {
return true;
}
// 默认放行地址
String url = request.getRequestURL().toString();
if (url.endsWith(".css") || url.endsWith(".js") || url.endsWith(".jsp")) {
return true;
}
return Stream.of(NOT_FILTER).anyMatch(url::contains);
}
@Override
public String filterName() {
return "sso";
}
@Override
public String[] urlPatterns() {
return new String[]{"/*"};
}
}