Browse Source

open

master
pioneer 2 years ago
commit
1d486f4135
  1. 6
      README.md
  2. BIN
      doc/JSD-9724-需求确认书V1.1.docx
  3. BIN
      doc/单点登录插件使用文档.docx
  4. BIN
      lib/finekit-10.0-20200828.jar
  5. 22
      plugin.xml
  6. 127
      src/main/java/com/fr/plugin/FJ1Filter.java
  7. 83
      src/main/java/com/fr/plugin/FLConfig.java
  8. 15
      src/main/java/com/fr/plugin/FLHttpHander.java
  9. 16
      src/main/java/com/fr/plugin/FLLifeCycleMonitor.java
  10. 24
      src/main/java/com/fr/plugin/FLPassportProvider.java
  11. 22
      src/main/java/com/fr/plugin/FLURLAliasBridge.java
  12. 34
      src/main/java/com/fr/plugin/LogindJSHandler.java
  13. 54
      src/main/java/com/fr/plugin/LoginedLogoComponent.java
  14. 25
      src/main/java/com/fr/plugin/Oauth2Bean.java
  15. 64
      src/main/java/com/fr/plugin/Oauth2Passport.java
  16. 161
      src/main/java/com/fr/plugin/handler/FLLoginCallBackHander.java
  17. 32
      src/main/resources/com/fr/plugin/web/logined.js

6
README.md

@ -0,0 +1,6 @@
# open-JSD-9724
JSD-9724 单点集成\
免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\
仅作为开发者学习参考使用!禁止用于任何商业用途!\
为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系【pioneer】处理。

BIN
doc/JSD-9724-需求确认书V1.1.docx

Binary file not shown.

BIN
doc/单点登录插件使用文档.docx

Binary file not shown.

BIN
lib/finekit-10.0-20200828.jar

Binary file not shown.

22
plugin.xml

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<plugin>
<id>com.fr.plugin.foauth.login</id>
<name><![CDATA[单点登录插件]]></name>
<active>yes</active>
<version>1.0.2</version>
<env-version>10.0</env-version>
<jartime>2020-07-31</jartime>
<vendor>fr.open</vendor>
<lifecycle-monitor class="com.fr.plugin.JCLifeCycleMonitor"/>
<extra-decision>
<GlobalRequestFilterProvider class="com.fr.plugin.FJ1Filter"/>
<PassportProvider class="com.fr.plugin.FLPassportProvider"/>
<WebResourceProvider class="com.fr.plugin.LogindJSHandler"/>
<!-- 长连接 -->
<HttpHandlerProvider class="com.fr.plugin.FLHttpHander"/>
<!-- 短连接 -->
<URLAliasProvider class="com.fr.plugin.FLURLAliasBridge"/>
</extra-decision>
<function-recorder class="com.fr.plugin.FLURLAliasBridge"/>
</plugin>

127
src/main/java/com/fr/plugin/FJ1Filter.java

@ -0,0 +1,127 @@
package com.fr.plugin;
import com.fanruan.api.security.SecurityKit;
import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider;
import com.fr.decision.webservice.v10.login.LoginService;
import com.fr.log.FineLoggerFactory;
import com.fr.plugin.transform.FunctionRecorder;
import com.fr.stable.StringUtils;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
@FunctionRecorder(localeKey = "fr2")
public class FJ1Filter extends AbstractGlobalRequestFilterProvider {
@Override
public String filterName() {
return "fj" ;
}
@Override
public String[] urlPatterns() {
return new String[]{
"/*"
};
}
@Override
public void init(FilterConfig filterConfig) {
FLConfig xtlConfig = FLConfig.getInstance();
FineLoggerFactory.getLogger().info("初始化");
super.init(filterConfig);
}
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse httpServletResponse, FilterChain filterChain) {
try {
if(needFilter(request) && !isLogin(request)) {
sendRedirect(httpServletResponse, goAuth());
return;
}
filterChain.doFilter(request, httpServletResponse);
} catch (IOException | ServletException e) {
printException2FrLog(e);
} catch (Exception e) {
e.printStackTrace();
}
}
private String goAuth() {
FLConfig xtlConfig = FLConfig.getInstance();
String valAddr = xtlConfig.getValAddr();
String service = xtlConfig.getService();
String appid = xtlConfig.getAppid();
String frurl = null;
try {
frurl = URLEncoder.encode(xtlConfig.getFrUrl() + "/url/oauth2/login","UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String url = String.format("%s/am/oauth2/authorize?service=%s&" +
"response_type=code&client_id=%s&" +
"scope=uid+cn+userIdCode&redirect_uri=%s&decision=Allow", valAddr, service,appid, frurl);
return url;
}
private boolean needFilter(HttpServletRequest request) {
String requestURI = request.getRequestURI();
String isAdmin = request.getParameter("isAdmin");
if (StringUtils.equals(isAdmin, "1")) {
return false;
}
if (StringUtils.isNotBlank(requestURI) && request.getMethod().equals("GET")) {
if (requestURI.endsWith("decision")) {
return true;
}
if (requestURI.endsWith("/view/form") || requestURI.endsWith("/view/report")) {
if (StringUtils.isNotBlank(request.getParameter("viewlet"))) {
return true;
}
}
if (requestURI.contains("/v10/entry/access/") && request.getMethod().equals("GET")) {
return true;
}
if (requestURI.contains("/v5/design/report") && (requestURI.endsWith("/edit") || requestURI.endsWith("/view"))) {
return true;
}
}
return false;
}
public static void printException2FrLog(Throwable e) {
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
String s = writer.toString();
FineLoggerFactory.getLogger().error("错误:{}", s);
}
private void sendRedirect(HttpServletResponse res, String url) {
res.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
res.setHeader("Location", url);
}
private void login(HttpServletRequest req, HttpServletResponse res, String username) {
String token = null;
try {
token = LoginService.getInstance().login(req, res, username);
req.setAttribute("fine_auth_token", token);
FineLoggerFactory.getLogger().error("login success");
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
FineLoggerFactory.getLogger().error("login failed");
}
}
private boolean isLogin(HttpServletRequest req) {
return LoginService.getInstance().isLogged(req);
}
}

83
src/main/java/com/fr/plugin/FLConfig.java

@ -0,0 +1,83 @@
package com.fr.plugin;
import com.fr.config.*;
import com.fr.config.holder.Conf;
import com.fr.config.holder.factory.Holders;
@Visualization(category = "单点配置")
public class FLConfig extends DefaultConfiguration {
private static volatile FLConfig config = null;
public static FLConfig getInstance() {
if (config == null) {
config = ConfigContext.getConfigInstance(FLConfig.class);
}
return config;
}
@Identifier(value = "valAddr", name = "接口地址", description = "接口地址", status = Status.SHOW)
private Conf<String> valAddr = Holders.simple("");
@Identifier(value = "service", name = "认证链名称,默认为:initService", description = "接口地址", status = Status.SHOW)
private Conf<String> service = Holders.simple("initService");
@Identifier(value = "appid", name = "应用的ClientId", description = "应用被推颁发的AppID", status = Status.SHOW)
private Conf<String> appid = Holders.simple("");
@Identifier(value = "loginClientSecret", name = "Client_secret", description = "Secret", status = Status.SHOW)
private Conf<String> loginClientSecret = Holders.simple("");
@Identifier(value = "frUrl", name = "当前fr系统地址", description = "", status = Status.SHOW)
private Conf<String> frUrl = Holders.simple("http://localhost:8075/webroot/decision");
public String getService() {
return service.get();
}
public void setService(String service) {
this.service.set(service);
}
public String getFrUrl() {
return frUrl.get();
}
public void setFrUrl(String frUrl) {
this.frUrl.set(frUrl);
}
public String getLoginClientSecret() {
return loginClientSecret.get();
}
public void setLoginClientSecret(String loginClientSecret) {
this.loginClientSecret.set(loginClientSecret);
}
public String getAppid() {
return appid.get();
}
public void setAppid(String appid) {
this.appid.set(appid);
}
public String getValAddr() {
return valAddr.get();
}
public void setValAddr(String valAddr) {
this.valAddr.set(valAddr);
}
@Override
public Object clone() throws CloneNotSupportedException {
FLConfig cloned = (FLConfig) super.clone();
cloned.valAddr = (Conf<String>) valAddr.clone();
cloned.service = (Conf<String>) service.clone();
cloned.appid = (Conf<String>) appid.clone();
cloned.loginClientSecret = (Conf<String>) loginClientSecret.clone();
cloned.frUrl = (Conf<String>) frUrl.clone();
return cloned;
}
}

15
src/main/java/com/fr/plugin/FLHttpHander.java

@ -0,0 +1,15 @@
package com.fr.plugin;
import com.fr.decision.fun.HttpHandler;
import com.fr.decision.fun.impl.AbstractHttpHandlerProvider;
import com.fr.plugin.handler.FLLoginCallBackHander;
public class FLHttpHander extends AbstractHttpHandlerProvider {
private HttpHandler[] actions = new HttpHandler[]{new FLLoginCallBackHander() };
@Override
public HttpHandler[] registerHandlers() {
return actions;
}
}

16
src/main/java/com/fr/plugin/FLLifeCycleMonitor.java

@ -0,0 +1,16 @@
package com.fr.plugin;
import com.fr.plugin.context.PluginContext;
import com.fr.plugin.observer.inner.AbstractPluginLifecycleMonitor;
public class FLLifeCycleMonitor extends AbstractPluginLifecycleMonitor {
@Override
public void afterRun(PluginContext pluginContext) {
FLConfig.getInstance();
}
@Override
public void beforeStop(PluginContext pluginContext) {
}
}

24
src/main/java/com/fr/plugin/FLPassportProvider.java

@ -0,0 +1,24 @@
package com.fr.plugin;
import com.fr.decision.authorize.Passport;
import com.fr.decision.fun.impl.AbstractPassportProvider;
import com.fr.decision.webservice.bean.authentication.PassportBean;
public class FLPassportProvider extends AbstractPassportProvider {
public static final String PASSPORT_TYPE = "oauth2";
@Override
public String passportType() {
return PASSPORT_TYPE;
}
@Override
public Class<? extends PassportBean> classForPassportBean() {
return Oauth2Bean.class;
}
@Override
public Class<? extends Passport> classForPassportConfig() {
return Oauth2Passport.class;
}
}

22
src/main/java/com/fr/plugin/FLURLAliasBridge.java

@ -0,0 +1,22 @@
package com.fr.plugin;
import com.fr.decision.fun.impl.AbstractURLAliasProvider;
import com.fr.decision.webservice.url.alias.URLAlias;
import com.fr.decision.webservice.url.alias.URLAliasFactory;
import com.fr.plugin.transform.ExecuteFunctionRecord;
import com.fr.plugin.transform.FunctionRecorder;
@FunctionRecorder
public class FLURLAliasBridge extends AbstractURLAliasProvider
{
@Override
@ExecuteFunctionRecord
public URLAlias[] registerAlias() {
//像这样配置之后再访问/api就可以通过http(s)://ip:port/webroot/decision/url/api。 进行访问
return new URLAlias[]{
URLAliasFactory.createPluginAlias("/oauth2/login", "/login", true),
};
}
}

34
src/main/java/com/fr/plugin/LogindJSHandler.java

@ -0,0 +1,34 @@
package com.fr.plugin;
import com.fr.decision.fun.impl.AbstractWebResourceProvider;
import com.fr.decision.web.MainComponent;
import com.fr.plugin.transform.ExecuteFunctionRecord;
import com.fr.plugin.transform.FunctionRecorder;
import com.fr.stable.fun.Authorize;
import com.fr.web.struct.Atom;
@Authorize(callSignKey = "com.fr.plugin.custom.logo" )
@FunctionRecorder
public class LogindJSHandler extends AbstractWebResourceProvider {
/**
* 需要附加到的主组件
*
* @return 主组件
*/
@Override
@ExecuteFunctionRecord
public Atom attach() {
return MainComponent.KEY;
}
/**
* 客户端所需的组件
*
* @return 组件
*/
@Override
public Atom client() {
return LoginedLogoComponent.KEY;
}
}

54
src/main/java/com/fr/plugin/LoginedLogoComponent.java

@ -0,0 +1,54 @@
package com.fr.plugin;
import com.fr.plugin.transform.ExecuteFunctionRecord;
import com.fr.plugin.transform.FunctionRecorder;
import com.fr.web.struct.Component;
import com.fr.web.struct.Filter;
import com.fr.web.struct.browser.RequestClient;
import com.fr.web.struct.category.ScriptPath;
import com.fr.web.struct.category.StylePath;
@FunctionRecorder()
public class LoginedLogoComponent extends Component {
public static final LoginedLogoComponent KEY = new LoginedLogoComponent();
/**
* 返回需要引入的JS脚本路径
* @param client 请求客户端描述
* @return JS脚本路径
*/
@ExecuteFunctionRecord
public ScriptPath script( RequestClient client ) {
PluginLicense pluginLicense = PluginLicenseManager.getInstance().getPluginLicenseByID("com.fr.plugin.custom.logo");
if (pluginLicense.isAvailable()) {
// 做认证通过的事情
return ScriptPath.build("com/fr/plugin/web/logined.js");
} else {
// 做认证未通过的事情
return ScriptPath.build("com/fr/plugin/web/webnuy.js");
}
}
/**
* 返回需要引入的CSS样式路径
* @param client 请求客户端描述
* @return CSS样式路径
*/
public StylePath style( RequestClient client ) {
//如果不需要就直接返回 StylePath.EMPTY;
return StylePath.EMPTY;
}
/**
* 通过给定的资源过滤器控制是否加载这个资源
* @return 资源过滤器
*/
public Filter filter() {
return new Filter(){
@Override
public boolean accept() {
//任何情况下我们都在平台组件加载时加载我们的组件
return true;
}
};
}
}

25
src/main/java/com/fr/plugin/Oauth2Bean.java

@ -0,0 +1,25 @@
package com.fr.plugin;
import com.fanruan.api.decision.auth.bean.BasePassportBean;
import com.fr.decision.authorize.Passport;
import com.fr.third.fasterxml.jackson.annotation.JsonSubTypes;
@JsonSubTypes.Type(value = Oauth2Bean.class, name = "LdapAuthenticBean")
public class Oauth2Bean extends BasePassportBean<Oauth2Passport>{
@Override
public String markType() {
return FLPassportProvider.PASSPORT_TYPE;
}
@Override
public BasePassportBean<Oauth2Passport> createPassportBean(Oauth2Passport oauth2Passport) {
return this;
}
@Override
public Passport createPassport() {
Oauth2Passport oauth2Passport = new Oauth2Passport();
return oauth2Passport;
}
}

64
src/main/java/com/fr/plugin/Oauth2Passport.java

@ -0,0 +1,64 @@
package com.fr.plugin;
import com.fanruan.api.conf.HolderKit;
import com.fanruan.api.decision.auth.BasePassport;
import com.fanruan.api.net.http.HttpKit;
import com.fanruan.api.util.AssistKit;
import com.fanruan.api.util.StringKit;
import com.fr.config.Identifier;
import com.fr.config.holder.Conf;
import com.fr.stable.StringUtils;
import java.io.IOException;
public class Oauth2Passport extends BasePassport {
@Override
public String markType() {
return FLPassportProvider.PASSPORT_TYPE;
}
@Identifier("emptytoken")
private Conf<String> emptytoken = HolderKit.simple(StringKit.EMPTY);
@Override
public int hashCode() {
return AssistKit.hashCode(emptytoken.get());
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getEmptytoken() {
return emptytoken.get();
}
public void setEmptytoken(String emptytoken) {
this.emptytoken.set(emptytoken);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Oauth2Passport)) {
return false;
}
Oauth2Passport target = (Oauth2Passport) obj;
return AssistKit.equals(target.getEmptytoken(), this.getEmptytoken());
}
@Override
public boolean checkTicket(String username, String inputPassword, String savedPassword, String hashPassword) {
//调用接口验证账号密码
FLConfig flConfig = FLConfig.getInstance();
String url = String.format("%s/am/identity/authenticate?username=%s&password=%s&uri=service=initService", flConfig.getValAddr(), username, inputPassword);
try {
String resp = HttpKit.get(url);
return StringUtils.contains(resp,"token.id=");
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}

161
src/main/java/com/fr/plugin/handler/FLLoginCallBackHander.java

@ -0,0 +1,161 @@
package com.fr.plugin.handler;
import com.fanruan.api.net.http.HttpKit;
import com.fr.data.NetworkHelper;
import com.fr.decision.authority.data.User;
import com.fr.decision.fun.impl.BaseHttpHandler;
import com.fr.decision.mobile.terminal.TerminalHandler;
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.ComparatorUtils;
import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory;
import com.fr.plugin.FLConfig;
import com.fr.security.JwtUtils;
import com.fr.stable.StringUtils;
import com.fr.stable.web.Device;
import com.fr.third.springframework.web.bind.annotation.RequestMethod;
import com.fr.web.utils.WebUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class FLLoginCallBackHander extends BaseHttpHandler {
@Override
public RequestMethod getMethod() {
return null;
}
@Override
public String getPath() {
return "/login";
}
@Override
public boolean isPublic() {
return true;
}
@Override
public void handle(HttpServletRequest req, HttpServletResponse res) throws Exception {
String token = req.getParameter("code");
if (StringUtils.isNotBlank(token)) {
String accessToken = getAccessToken(token);
if (StringUtils.isEmpty(accessToken)) {
WebUtils.printAsString(res, " 通过" + token + "获取accessToken失败返回内容无效");
return;
}
String userId = getUserId(accessToken);
if (StringUtils.isEmpty(userId)) {
WebUtils.printAsString(res, " 通过" + accessToken + "获取用户失败返回内容无效");
return;
}
UserService userService = UserService.getInstance();
User user = userService.getUserByUserName(userId);
if (user != null) {
login(req, res, userId);
FLConfig xtlConfig = FLConfig.getInstance();
String frUrl = xtlConfig.getFrUrl();
sendRedirect(res, frUrl);
return;
} else {
WebUtils.printAsString(res, "用户" + userId + "在帆软系统中不存在");
return;
}
} else {
sendRedirect(res, goAuth());
}
}
private String getUserId(String accessToken) throws IOException {
FLConfig xtlConfig = FLConfig.getInstance();
String valAddr = xtlConfig.getValAddr();
String url = String.format("%s/am/oauth2/tokeninfo?access_token=%s", valAddr,accessToken);
Map<String, String> header = new HashMap<>();
String resp = HttpKit.get(url, new HashMap<>(), header);
FineLoggerFactory.getLogger().info("访问getUserInfo返回:{}", resp);
JSONObject entries = new JSONObject(resp);
return entries.getString("uid");
}
private String getAccessToken(String code) throws IOException {
FLConfig xtlConfig = FLConfig.getInstance();
String valAddr = xtlConfig.getValAddr();
String appid = xtlConfig.getAppid();
String loginClientSecret = xtlConfig.getLoginClientSecret();
String url = String.format("%s/am/oauth2/access_token", valAddr);
Map<String, String> header = new HashMap<>();
Map<String, Object> params = new HashMap<>();
params.put("client_id", appid);
String frurl = xtlConfig.getFrUrl() + "/url/oauth2/login";
params.put("redirect_uri", frurl);
params.put("scope", "uid+cn+userIdCode");
params.put("client_secret", loginClientSecret);
params.put("grant_type", "authorization_code");
params.put("code", code);
String resp = HttpKit.post(url, params, "utf-8", "utf-8", header);
FineLoggerFactory.getLogger().info("访问getAccessToken返回:{}", resp);
JSONObject entries = new JSONObject(resp);
return entries.getString("access_token");
}
private String goAuth() {
FLConfig xtlConfig = FLConfig.getInstance();
String valAddr = xtlConfig.getValAddr();
String service = xtlConfig.getService();
String appid = xtlConfig.getAppid();
String frurl = xtlConfig.getFrUrl() + "/url/oauth2/login";
String url = String.format("%s/am/oauth2/authorize?service=%s&" +
"response_type=code&client_id=%s&" +
"scope=uid+cn+userIdCode&redirect_uri=%s&decision=Allow", valAddr, service,appid, frurl);
return url;
}
private void sendRedirect(HttpServletResponse res, String url) {
res.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
res.setHeader("Location", url);
}
private boolean login(HttpServletRequest req, HttpServletResponse res, String username) {
try {
String oldToken = TokenResource.COOKIE.getToken(req);
if ((oldToken == null) || (!checkTokenValid(req, oldToken, username))) {
HttpSession session = req.getSession(true);
String token = LoginService.getInstance().login(req, res, username);
session.setAttribute("fine_auth_token", token);
FineLoggerFactory.getLogger().error("fr CookieFilter is over with username is ###" + username);
return true;
} else {
FineLoggerFactory.getLogger().error("no need login: {}", username);
return true;
}
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
return false;
}
}
private boolean checkTokenValid(HttpServletRequest req, String token, String currentUserName) {
try {
if (!ComparatorUtils.equals(currentUserName, JwtUtils.parseJWT(token).getSubject())) {
FineLoggerFactory.getLogger().info("username changed:" + currentUserName);
return false;
} else {
Device device = NetworkHelper.getDevice(req);
LoginService.getInstance().loginStatusValid(token, TerminalHandler.getTerminal(req, device));
return true;
}
} catch (Exception var5) {
return false;
}
}
}

32
src/main/resources/com/fr/plugin/web/logined.js

@ -0,0 +1,32 @@
;(function ($) {
BI.config("dec.user.setting.authentications", function (items) {
items.push({
value: "oauth2",
text: "统一认证",
"@class": "com.fr.plugin.Oauth2Bean",
component: {
type: "dec.plugin.custom_oatuh2"
}
});
return items;
});
var Ldaps = BI.inherit(BI.Widget, {
props: {
baseCls: "",
},
render: function () {
var self = this, o = this.options;
return {
type: "bi.vertical",
bgap: 15,
items: []
}
},
getValue: function () {
return {};
}
});
BI.shortcut("dec.plugin.custom_oatuh2", Ldaps);
})(jQuery)
Loading…
Cancel
Save