Unverified 提交 c03b82fd authored 作者: fit2cloud-chenyw's avatar fit2cloud-chenyw 提交者: GitHub

Merge pull request #813 from dataease/pr@dev@feat_oidc_sso_login

feat: sso单点登录
...@@ -252,11 +252,11 @@ ...@@ -252,11 +252,11 @@
<version>20171018</version> <version>20171018</version>
</dependency> </dependency>
<dependency> <!--<dependency>
<groupId>org.apache.httpcomponents</groupId> <groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId> <artifactId>httpclient</artifactId>
<version>4.5.13</version> <version>4.5.13</version>
</dependency> </dependency>-->
<!-- 反射工具包 --> <!-- 反射工具包 -->
<dependency> <dependency>
<groupId>net.oneandone.reflections8</groupId> <groupId>net.oneandone.reflections8</groupId>
......
...@@ -4,9 +4,7 @@ import com.github.xiaoymin.knife4j.annotations.ApiSupport; ...@@ -4,9 +4,7 @@ import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.dataease.auth.api.dto.CurrentUserDto; import io.dataease.auth.api.dto.CurrentUserDto;
import io.dataease.auth.api.dto.LoginDto; import io.dataease.auth.api.dto.LoginDto;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
...@@ -46,4 +44,9 @@ public interface AuthApi { ...@@ -46,4 +44,9 @@ public interface AuthApi {
@PostMapping("/isOpenLdap") @PostMapping("/isOpenLdap")
boolean isOpenLdap(); boolean isOpenLdap();
@ApiOperation("是否开启oidc")
@PostMapping("/isOpenOidc")
boolean isOpenOidc();
} }
...@@ -13,6 +13,8 @@ public class TokenInfo implements Serializable { ...@@ -13,6 +13,8 @@ public class TokenInfo implements Serializable {
private Long userId; private Long userId;
/* private String idToken; */
public String format(){ public String format(){
return username + "," +userId; return username + "," +userId;
} }
......
...@@ -4,7 +4,6 @@ import org.apache.shiro.subject.Subject; ...@@ -4,7 +4,6 @@ import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.LogoutFilter; import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
......
...@@ -21,17 +21,19 @@ import io.dataease.plugins.util.PluginUtils; ...@@ -21,17 +21,19 @@ import io.dataease.plugins.util.PluginUtils;
import io.dataease.plugins.xpack.ldap.dto.request.LdapValidateRequest; import io.dataease.plugins.xpack.ldap.dto.request.LdapValidateRequest;
import io.dataease.plugins.xpack.ldap.dto.response.ValidateResult; import io.dataease.plugins.xpack.ldap.dto.response.ValidateResult;
import io.dataease.plugins.xpack.ldap.service.LdapXpackService; import io.dataease.plugins.xpack.ldap.service.LdapXpackService;
import io.dataease.plugins.xpack.oidc.service.OidcXpackService;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest;
@RestController @RestController
public class AuthServer implements AuthApi { public class AuthServer implements AuthApi {
...@@ -113,6 +115,13 @@ public class AuthServer implements AuthApi { ...@@ -113,6 +115,13 @@ public class AuthServer implements AuthApi {
@Override @Override
public String logout() { public String logout() {
String token = ServletUtils.getToken(); String token = ServletUtils.getToken();
if (isOpenOidc()) {
HttpServletRequest request = ServletUtils.request();
String idToken = request.getHeader("IdToken");
OidcXpackService oidcXpackService = SpringContextUtil.getBean(OidcXpackService.class);
oidcXpackService.logout(idToken);
}
if (StringUtils.isEmpty(token) || StringUtils.equals("null", token) || StringUtils.equals("undefined", token)) { if (StringUtils.isEmpty(token) || StringUtils.equals("null", token) || StringUtils.equals("undefined", token)) {
return "success"; return "success";
} }
...@@ -144,6 +153,15 @@ public class AuthServer implements AuthApi { ...@@ -144,6 +153,15 @@ public class AuthServer implements AuthApi {
return open; return open;
} }
@Override
public boolean isOpenOidc() {
Boolean licValid = PluginUtils.licValid();
if(!licValid) return false;
return authUserService.supportOidc();
}
/*@Override /*@Override
public Boolean isLogin() { public Boolean isLogin() {
return null; return null;
......
...@@ -23,6 +23,8 @@ public interface AuthUserService { ...@@ -23,6 +23,8 @@ public interface AuthUserService {
boolean supportLdap(); boolean supportLdap();
Boolean supportOidc();
} }
...@@ -10,6 +10,8 @@ import io.dataease.commons.constants.AuthConstants; ...@@ -10,6 +10,8 @@ import io.dataease.commons.constants.AuthConstants;
import io.dataease.commons.utils.LogUtil; import io.dataease.commons.utils.LogUtil;
import io.dataease.plugins.config.SpringContextUtil; import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.xpack.ldap.service.LdapXpackService; import io.dataease.plugins.xpack.ldap.service.LdapXpackService;
import io.dataease.plugins.xpack.oidc.service.OidcXpackService;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CacheEvict;
...@@ -114,4 +116,15 @@ public class AuthUserServiceImpl implements AuthUserService { ...@@ -114,4 +116,15 @@ public class AuthUserServiceImpl implements AuthUserService {
if(ObjectUtils.isEmpty(ldapXpackService)) return false; if(ObjectUtils.isEmpty(ldapXpackService)) return false;
return ldapXpackService.isOpen(); return ldapXpackService.isOpen();
} }
@Override
public Boolean supportOidc() {
Map<String, OidcXpackService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType((OidcXpackService.class));
if(beansOfType.keySet().size() == 0) return false;
OidcXpackService oidcXpackService = SpringContextUtil.getBean(OidcXpackService.class);
if(ObjectUtils.isEmpty(oidcXpackService)) return false;
return oidcXpackService.isSuuportOIDC();
}
} }
...@@ -56,9 +56,15 @@ public class ShiroServiceImpl implements ShiroService { ...@@ -56,9 +56,15 @@ public class ShiroServiceImpl implements ShiroService {
// filterChainDefinitionMap.put("/axios.map", ANON); // filterChainDefinitionMap.put("/axios.map", ANON);
filterChainDefinitionMap.put("/api/auth/login", ANON); filterChainDefinitionMap.put("/api/auth/login", ANON);
filterChainDefinitionMap.put("/api/auth/logout", ANON); // filterChainDefinitionMap.put("/api/auth/logout", ANON);
filterChainDefinitionMap.put("/api/auth/validateName", ANON); filterChainDefinitionMap.put("/api/auth/validateName", ANON);
filterChainDefinitionMap.put("/api/auth/isOpenLdap", ANON); filterChainDefinitionMap.put("/api/auth/isOpenLdap", ANON);
filterChainDefinitionMap.put("/api/auth/isOpenOidc", ANON);
filterChainDefinitionMap.put("/api/pluginCommon/component/*", ANON);
filterChainDefinitionMap.put("/plugin/oidc/authInfo", ANON);
filterChainDefinitionMap.put("/sso/callBack*", ANON);
filterChainDefinitionMap.put("/unauth", ANON); filterChainDefinitionMap.put("/unauth", ANON);
filterChainDefinitionMap.put("/display/**", ANON); filterChainDefinitionMap.put("/display/**", ANON);
filterChainDefinitionMap.put("/tokenExpired", ANON); filterChainDefinitionMap.put("/tokenExpired", ANON);
......
...@@ -2,16 +2,18 @@ package io.dataease.auth.util; ...@@ -2,16 +2,18 @@ package io.dataease.auth.util;
import com.auth0.jwt.JWT; import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.JWTCreator.Builder;
import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.Verification;
import io.dataease.auth.entity.TokenInfo; import io.dataease.auth.entity.TokenInfo;
import io.dataease.auth.entity.TokenInfo.TokenInfoBuilder;
import io.dataease.commons.utils.CommonBeanFactory; import io.dataease.commons.utils.CommonBeanFactory;
import io.dataease.exception.DataEaseException; import io.dataease.exception.DataEaseException;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import java.util.Date; import java.util.Date;
...@@ -34,10 +36,13 @@ public class JWTUtils { ...@@ -34,10 +36,13 @@ public class JWTUtils {
*/ */
public static boolean verify(String token, TokenInfo tokenInfo, String secret) { public static boolean verify(String token, TokenInfo tokenInfo, String secret) {
Algorithm algorithm = Algorithm.HMAC256(secret); Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm) Verification verification = JWT.require(algorithm)
.withClaim("username", tokenInfo.getUsername()) .withClaim("username", tokenInfo.getUsername())
.withClaim("userId", tokenInfo.getUserId()) .withClaim("userId", tokenInfo.getUserId());
.build(); /* if (StringUtils.isNotBlank(tokenInfo.getIdToken())) {
verification.withClaim("idToken", tokenInfo.getIdToken());
} */
JWTVerifier verifier = verification.build();
verifier.verify(token); verifier.verify(token);
return true; return true;
} }
...@@ -50,10 +55,15 @@ public class JWTUtils { ...@@ -50,10 +55,15 @@ public class JWTUtils {
DecodedJWT jwt = JWT.decode(token); DecodedJWT jwt = JWT.decode(token);
String username = jwt.getClaim("username").asString(); String username = jwt.getClaim("username").asString();
Long userId = jwt.getClaim("userId").asLong(); Long userId = jwt.getClaim("userId").asLong();
// String idToken = jwt.getClaim("idToken").asString();
if (StringUtils.isEmpty(username) || ObjectUtils.isEmpty(userId) ){ if (StringUtils.isEmpty(username) || ObjectUtils.isEmpty(userId) ){
DataEaseException.throwException("token格式错误!"); DataEaseException.throwException("token格式错误!");
} }
TokenInfo tokenInfo = TokenInfo.builder().username(username).userId(userId).build(); TokenInfoBuilder tokenInfoBuilder = TokenInfo.builder().username(username).userId(userId);
/* if (StringUtils.isNotBlank(idToken)) {
tokenInfoBuilder.idToken(idToken);
} */
TokenInfo tokenInfo = tokenInfoBuilder.build();
return tokenInfo; return tokenInfo;
} }
...@@ -107,12 +117,14 @@ public class JWTUtils { ...@@ -107,12 +117,14 @@ public class JWTUtils {
try { try {
Date date = new Date(System.currentTimeMillis()+EXPIRE_TIME); Date date = new Date(System.currentTimeMillis()+EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(secret); Algorithm algorithm = Algorithm.HMAC256(secret);
// 附带username信息 Builder builder = JWT.create()
return JWT.create()
.withClaim("username", tokenInfo.getUsername()) .withClaim("username", tokenInfo.getUsername())
.withClaim("userId", tokenInfo.getUserId()) .withClaim("userId", tokenInfo.getUserId());
.withExpiresAt(date) /* if (StringUtils.isNotBlank(tokenInfo.getIdToken())) {
.sign(algorithm); builder.withClaim("idToken", tokenInfo.getIdToken());
} */
return builder.withExpiresAt(date).sign(algorithm);
} catch (Exception e) { } catch (Exception e) {
return null; return null;
} }
......
...@@ -2,6 +2,7 @@ package io.dataease.plugins.config; ...@@ -2,6 +2,7 @@ package io.dataease.plugins.config;
import io.dataease.base.domain.MyPlugin; import io.dataease.base.domain.MyPlugin;
import io.dataease.plugins.loader.ClassloaderResponsity; import io.dataease.plugins.loader.ClassloaderResponsity;
import io.dataease.plugins.loader.ControllerLoader;
import io.dataease.plugins.loader.ModuleClassLoader; import io.dataease.plugins.loader.ModuleClassLoader;
import io.dataease.plugins.loader.MybatisLoader; import io.dataease.plugins.loader.MybatisLoader;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
...@@ -19,6 +20,9 @@ public class LoadjarUtil { ...@@ -19,6 +20,9 @@ public class LoadjarUtil {
@Autowired @Autowired
private MybatisLoader mybatisLoader; private MybatisLoader mybatisLoader;
@Autowired
private ControllerLoader controllerLoader;
public List<?> loadJar(String jarPath, MyPlugin myPlugin) throws Exception{ public List<?> loadJar(String jarPath, MyPlugin myPlugin) throws Exception{
File jar = new File(jarPath); File jar = new File(jarPath);
URI uri = jar.toURI(); URI uri = jar.toURI();
...@@ -34,6 +38,10 @@ public class LoadjarUtil { ...@@ -34,6 +38,10 @@ public class LoadjarUtil {
Thread.currentThread().setContextClassLoader(classLoader); Thread.currentThread().setContextClassLoader(classLoader);
classLoader.initBean(); classLoader.initBean();
mybatisLoader.loadMybatis(myPlugin); mybatisLoader.loadMybatis(myPlugin);
List<String> controllers = classLoader.getRegisteredController();
controllerLoader.registerController(controllers);
ClassloaderResponsity.getInstance().addClassLoader(moduleName,classLoader); ClassloaderResponsity.getInstance().addClassLoader(moduleName,classLoader);
......
package io.dataease.plugins.loader;
import io.dataease.commons.utils.LogUtil;
import io.dataease.plugins.config.SpringContextUtil;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Method;
import java.util.List;
@Component
public class ControllerLoader {
/**
* 去掉Controller的Mapping
* @param controllerBeanName
*/
private void unregisterController(String controllerBeanName){
final RequestMappingHandlerMapping requestMappingHandlerMapping=(RequestMappingHandlerMapping)SpringContextUtil.getBean("requestMappingHandlerMapping");
if(requestMappingHandlerMapping!=null){
String handler=controllerBeanName;
Object controller= SpringContextUtil.getBean(handler);
if(controller==null){
return;
}
final Class<?> targetClass=controller.getClass();
ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
try {
Method createMappingMethod = RequestMappingHandlerMapping.class.
getDeclaredMethod("getMappingForMethod", Method.class, Class.class);
createMappingMethod.setAccessible(true);
RequestMappingInfo requestMappingInfo =(RequestMappingInfo)
createMappingMethod.invoke(requestMappingHandlerMapping,specificMethod,targetClass);
if(requestMappingInfo != null) {
requestMappingHandlerMapping.unregisterMapping(requestMappingInfo);
}
}catch (Exception e){
e.printStackTrace();
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
}
/**
* 注册Controller
* @param controllerBeanName
* @throws Exception
*/
private void registerController(String controllerBeanName) throws Exception{
final RequestMappingHandlerMapping requestMappingHandlerMapping=(RequestMappingHandlerMapping) SpringContextUtil.getBean("requestMappingHandlerMapping");
if(requestMappingHandlerMapping!=null){
String handler=controllerBeanName;
Object controller= SpringContextUtil.getBean(handler);
if(controller==null){
return;
}
unregisterController(controllerBeanName);
//注册Controller
Method method=requestMappingHandlerMapping.getClass().getSuperclass().getSuperclass().getDeclaredMethod("detectHandlerMethods",Object.class);
method.setAccessible(true);
method.invoke(requestMappingHandlerMapping,handler);
}
}
public void registerController(List<String> beanNames) {
beanNames.forEach(name -> {
try {
registerController(name);
} catch (Exception e) {
// e.printStackTrace();
LogUtil.error(e);
}
});
}
}
...@@ -7,10 +7,14 @@ import org.apache.ibatis.session.SqlSessionFactory; ...@@ -7,10 +7,14 @@ import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.TypeAliasRegistry; import org.apache.ibatis.type.TypeAliasRegistry;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
...@@ -35,6 +39,8 @@ public class ModuleClassLoader extends URLClassLoader { ...@@ -35,6 +39,8 @@ public class ModuleClassLoader extends URLClassLoader {
//需要注册的spring bean的name集合 //需要注册的spring bean的name集合
private List<String> registeredBean = new ArrayList<>(); private List<String> registeredBean = new ArrayList<>();
private List<String> registeredController = new ArrayList<>();
//构造 //构造
public ModuleClassLoader(URL[] urls, ClassLoader parent) { public ModuleClassLoader(URL[] urls, ClassLoader parent) {
...@@ -150,8 +156,12 @@ public class ModuleClassLoader extends URLClassLoader { ...@@ -150,8 +156,12 @@ public class ModuleClassLoader extends URLClassLoader {
beanName = StringUtils.uncapitalize(beanName); beanName = StringUtils.uncapitalize(beanName);
SpringContextUtil.getBeanFactory().registerBeanDefinition(beanName,beanDefinition); SpringContextUtil.getBeanFactory().registerBeanDefinition(beanName,beanDefinition);
if (isHandler(cla)) {
registeredController.add(beanName);
}
registeredBean.add(beanName); registeredBean.add(beanName);
// System.out.println("注册bean:"+beanName);
} }
} }
...@@ -164,6 +174,10 @@ public class ModuleClassLoader extends URLClassLoader { ...@@ -164,6 +174,10 @@ public class ModuleClassLoader extends URLClassLoader {
return registeredBean; return registeredBean;
} }
public List<String> getRegisteredController() {
return registeredController;
}
/** /**
* 方法描述 判断class对象是否带有spring的注解 * 方法描述 判断class对象是否带有spring的注解
...@@ -184,6 +198,9 @@ public class ModuleClassLoader extends URLClassLoader { ...@@ -184,6 +198,9 @@ public class ModuleClassLoader extends URLClassLoader {
if( Modifier.isAbstract(cla.getModifiers())){ if( Modifier.isAbstract(cla.getModifiers())){
return false; return false;
} }
if (isHandler(cla)) {
return true;
}
if(cla.getAnnotation(Component.class)!=null){ if(cla.getAnnotation(Component.class)!=null){
return true; return true;
...@@ -194,8 +211,15 @@ public class ModuleClassLoader extends URLClassLoader { ...@@ -194,8 +211,15 @@ public class ModuleClassLoader extends URLClassLoader {
if(cla.getAnnotation(Service.class)!=null){ if(cla.getAnnotation(Service.class)!=null){
return true; return true;
} }
if(cla.getAnnotation(Service.class)!=null){
return true;
}
return false; return false;
} }
protected boolean isHandler(Class<?> beanType) {
return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class);
}
} }
...@@ -2,6 +2,7 @@ package io.dataease.plugins.server; ...@@ -2,6 +2,7 @@ package io.dataease.plugins.server;
import io.dataease.commons.utils.ServletUtils; import io.dataease.commons.utils.ServletUtils;
import io.dataease.plugins.common.dto.PluginSysMenu; import io.dataease.plugins.common.dto.PluginSysMenu;
import io.dataease.plugins.common.service.PluginComponentService;
import io.dataease.plugins.common.service.PluginMenuService; import io.dataease.plugins.common.service.PluginMenuService;
import io.dataease.plugins.config.SpringContextUtil; import io.dataease.plugins.config.SpringContextUtil;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
...@@ -9,7 +10,6 @@ import org.springframework.web.bind.annotation.PathVariable; ...@@ -9,7 +10,6 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore; import springfox.documentation.annotations.ApiIgnore;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.IOException; import java.io.IOException;
...@@ -25,7 +25,7 @@ import java.util.concurrent.atomic.AtomicReference; ...@@ -25,7 +25,7 @@ import java.util.concurrent.atomic.AtomicReference;
public class PluginCommonServer { public class PluginCommonServer {
@GetMapping("/async/{menuId}") @GetMapping("/async/{menuId}")
public void componentInfo(@PathVariable Long menuId) { public void menuInfo(@PathVariable Long menuId) {
Map<String, PluginMenuService> pluginMenuServiceMap = SpringContextUtil.getApplicationContext().getBeansOfType(PluginMenuService.class); Map<String, PluginMenuService> pluginMenuServiceMap = SpringContextUtil.getApplicationContext().getBeansOfType(PluginMenuService.class);
pluginMenuServiceMap.values().stream().forEach(service -> { pluginMenuServiceMap.values().stream().forEach(service -> {
AtomicReference<PluginSysMenu> atomicReference = new AtomicReference<>(); AtomicReference<PluginSysMenu> atomicReference = new AtomicReference<>();
...@@ -65,4 +65,41 @@ public class PluginCommonServer { ...@@ -65,4 +65,41 @@ public class PluginCommonServer {
return; return;
}); });
} }
@GetMapping("/component/{componentName}")
public void componentInfo(@PathVariable String componentName) {
Map<String, PluginComponentService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType(PluginComponentService.class);
beansOfType.values().stream().forEach(service -> {
List<String> components = service.components();
if (components.contains(componentName)) {
HttpServletResponse response = ServletUtils.response();
BufferedInputStream bis = null;
InputStream inputStream = null;
OutputStream os = null; //输出流
try{
inputStream = service.vueResource(componentName);
byte[] buffer = new byte[1024];
os = response.getOutputStream();
bis = new BufferedInputStream(inputStream);
int i = bis.read(buffer);
while(i != -1){
os.write(buffer, 0, i);
i = bis.read(buffer);
}
os.flush();
}catch (Exception e) {
e.printStackTrace();
}finally {
try {
bis.close();
inputStream.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return;
}
});
}
} }
package io.dataease.plugins.server;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import io.dataease.auth.entity.SysUserEntity;
import io.dataease.auth.entity.TokenInfo;
import io.dataease.auth.service.AuthUserService;
import io.dataease.auth.util.JWTUtils;
import io.dataease.commons.exception.DEException;
import io.dataease.commons.utils.CodingUtil;
import io.dataease.commons.utils.ServletUtils;
import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.xpack.display.dto.response.SysSettingDto;
import io.dataease.plugins.xpack.oidc.dto.SSOToken;
import io.dataease.plugins.xpack.oidc.dto.SSOUserInfo;
import io.dataease.plugins.xpack.oidc.service.OidcXpackService;
import io.dataease.service.sys.SysUserService;
@RequestMapping("/sso")
@Controller
public class SSOServer {
@Autowired
private AuthUserService authUserService;
@Autowired
private SysUserService sysUserService;
@GetMapping("/callBack")
public ModelAndView callBack(@RequestParam("code") String code, @RequestParam("state") String state) {
Map<String, OidcXpackService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType((OidcXpackService.class));
if(beansOfType.keySet().size() == 0) {
DEException.throwException("缺少oidc插件");
}
OidcXpackService oidcXpackService = SpringContextUtil.getBean(OidcXpackService.class);
Boolean suuportOIDC = oidcXpackService.isSuuportOIDC();
if (!suuportOIDC) {
DEException.throwException("未开启oidc");
}
Map<String, String> config = config(oidcXpackService);
SSOToken ssoToken = oidcXpackService.requestSsoToken(config, code, state);
SSOUserInfo ssoUserInfo = oidcXpackService.requestUserInfo(config, ssoToken.getAccessToken());
SysUserEntity sysUserEntity = authUserService.getUserByName(ssoUserInfo.getUserName());
if(null == sysUserEntity){
sysUserService.saveOIDCUser(ssoUserInfo);
sysUserEntity = authUserService.getUserByName(ssoUserInfo.getUserName());
}
TokenInfo tokenInfo = TokenInfo.builder().userId(sysUserEntity.getUserId()).username(sysUserEntity.getUsername()).build();
String realPwd = CodingUtil.md5(sysUserService.defaultPWD());
String token = JWTUtils.sign(tokenInfo, realPwd);
ServletUtils.setToken(token);
HttpServletResponse response = ServletUtils.response();
Cookie cookie_token = new Cookie("Authorization", token);cookie_token.setPath("/");
Cookie cookie_id_token = new Cookie("IdToken", ssoToken.getIdToken());cookie_id_token.setPath("/");
Cookie cookie_ac_token = new Cookie("AccessToken", ssoToken.getAccessToken());cookie_ac_token.setPath("/");
response.addCookie(cookie_token);
response.addCookie(cookie_id_token);
response.addCookie(cookie_ac_token);
ModelAndView modelAndView = new ModelAndView("redirect:/");
return modelAndView;
}
private Map<String, String> config(OidcXpackService oidcXpackService) {
List<SysSettingDto> sysSettingDtos = oidcXpackService.oidcSettings();
Map<String, String> config = sysSettingDtos.stream().collect(Collectors.toMap(SysSettingDto::getParamKey, SysSettingDto::getParamValue));
return config;
}
}
...@@ -4,9 +4,11 @@ package io.dataease.plugins.server; ...@@ -4,9 +4,11 @@ package io.dataease.plugins.server;
import io.dataease.plugins.config.SpringContextUtil; import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.xpack.display.dto.response.SysSettingDto; import io.dataease.plugins.xpack.display.dto.response.SysSettingDto;
import io.dataease.plugins.xpack.oidc.service.OidcXpackService; import io.dataease.plugins.xpack.oidc.service.OidcXpackService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
@RequestMapping("/plugin/oidc") @RequestMapping("/plugin/oidc")
@RestController @RestController
...@@ -24,4 +26,34 @@ public class XOidcServer { ...@@ -24,4 +26,34 @@ public class XOidcServer {
OidcXpackService oidcXpackService = SpringContextUtil.getBean(OidcXpackService.class); OidcXpackService oidcXpackService = SpringContextUtil.getBean(OidcXpackService.class);
oidcXpackService.save(settings); oidcXpackService.save(settings);
} }
@PostMapping(value="/authInfo")
public Map<String, Object> authInfo() {
OidcXpackService oidcXpackService = SpringContextUtil.getBean(OidcXpackService.class);
Map<String, Object> result = new HashMap<String, Object>();
List<SysSettingDto> oidcSettings = oidcXpackService.oidcSettings();
Map<String, String> authParam = new HashMap<>();
authParam.put("response_type", "code");
authParam.put("state", "state");
// authParam.put("redirect_uri", "http://localhost:9528");
oidcSettings.forEach(param -> {
if(StringUtils.isNotBlank(param.getParamKey())) {
if (StringUtils.equals(param.getParamKey(), "oidc.authEndpoint")) {
result.put("authEndpoint", param.getParamValue());
}
if (StringUtils.equals(param.getParamKey(), "oidc.scope")) {
authParam.put("scope", param.getParamValue());
}
if (StringUtils.equals(param.getParamKey(), "oidc.clientId")) {
authParam.put("client_id", param.getParamValue());
}
}
});
result.put("authParam", authParam);
return result;
}
} }
...@@ -22,6 +22,8 @@ import io.dataease.controller.sys.response.SysUserGridResponse; ...@@ -22,6 +22,8 @@ import io.dataease.controller.sys.response.SysUserGridResponse;
import io.dataease.controller.sys.response.SysUserRole; import io.dataease.controller.sys.response.SysUserRole;
import io.dataease.i18n.Translator; import io.dataease.i18n.Translator;
import io.dataease.plugins.common.entity.XpackLdapUserEntity; import io.dataease.plugins.common.entity.XpackLdapUserEntity;
import io.dataease.plugins.xpack.oidc.dto.SSOUserInfo;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
...@@ -31,6 +33,8 @@ import org.springframework.stereotype.Service; ...@@ -31,6 +33,8 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -103,6 +107,33 @@ public class SysUserService { ...@@ -103,6 +107,33 @@ public class SysUserService {
return insert; return insert;
} }
@Transactional
public void saveOIDCUser(SSOUserInfo ssoUserInfo) {
long now = System.currentTimeMillis();
SysUser sysUser = new SysUser();
sysUser.setUsername(ssoUserInfo.getUserName());
sysUser.setNickName(ssoUserInfo.getNickName());
sysUser.setEmail(ssoUserInfo.getEmail());
sysUser.setPassword(CodingUtil.md5(DEFAULT_PWD));
sysUser.setCreateTime(now);
sysUser.setUpdateTime(now);
sysUser.setEnabled(1L);
sysUser.setLanguage("zh_CN");
sysUser.setFrom(2);
sysUserMapper.insert(sysUser);
SysUser dbUser = findOne(sysUser);
if (null != dbUser && null != dbUser.getUserId()) {
// oidc默认角色是普通员工
List<Long> roleIds = new ArrayList<Long>();
roleIds.add(2L);
saveUserRoles( dbUser.getUserId(), roleIds);
}
}
public String defaultPWD() {
return DEFAULT_PWD;
}
@Transactional @Transactional
public void saveLdapUsers(LdapAddRequest request) { public void saveLdapUsers(LdapAddRequest request) {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
...@@ -116,6 +147,7 @@ public class SysUserService { ...@@ -116,6 +147,7 @@ public class SysUserService {
sysUser.setCreateTime(now); sysUser.setCreateTime(now);
sysUser.setUpdateTime(now); sysUser.setUpdateTime(now);
sysUser.setEnabled(request.getEnabled()); sysUser.setEnabled(request.getEnabled());
sysUser.setLanguage("zh_CN");
sysUser.setFrom(1); sysUser.setFrom(1);
return sysUser; return sysUser;
}).collect(Collectors.toList()); }).collect(Collectors.toList());
......
...@@ -57,3 +57,10 @@ export function ldapStatus() { ...@@ -57,3 +57,10 @@ export function ldapStatus() {
method: 'post' method: 'post'
}) })
} }
export function oidcStatus() {
return request({
url: '/api/auth/isOpenOidc',
method: 'post'
})
}
...@@ -15,11 +15,11 @@ ...@@ -15,11 +15,11 @@
{{ $t('login.welcome') + (uiInfo && uiInfo['ui.title'] && uiInfo['ui.title'].paramValue || ' DataEase') }} {{ $t('login.welcome') + (uiInfo && uiInfo['ui.title'] && uiInfo['ui.title'].paramValue || ' DataEase') }}
</div> </div>
<div class="login-form"> <div class="login-form">
<el-form-item v-if="openLdap"> <el-form-item v-if="loginTypes.length > 1">
<el-radio-group v-if="openLdap" v-model="loginForm.loginType"> <el-radio-group v-if="loginTypes.length > 1" v-model="loginForm.loginType" @change="changeLoginType">
<el-radio v-if="openLdap" :label="0" size="mini">普通登录</el-radio> <el-radio :label="0" size="mini">普通登录</el-radio>
<el-radio v-if="openLdap" :label="1" size="mini">LDAP</el-radio> <el-radio v-if="loginTypes.includes(1)" :label="1" size="mini">LDAP</el-radio>
<el-radio v-if="loginTypes.includes(2)" :label="2" size="mini">OIDC</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item prop="username"> <el-form-item prop="username">
...@@ -56,41 +56,20 @@ ...@@ -56,41 +56,20 @@
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
<plugin-com v-if="loginTypes.includes(2) && loginForm.loginType === 2" ref="SSOComponent" component-name="SSOComponent" />
</div> </div>
</template> </template>
<script> <script>
import { encrypt } from '@/utils/rsaEncrypt' import { encrypt } from '@/utils/rsaEncrypt'
import { ldapStatus } from '@/api/user' import { ldapStatus, oidcStatus } from '@/api/user'
import { getSysUI } from '@/utils/auth' import { getSysUI } from '@/utils/auth'
import PluginCom from '@/views/system/plugin/PluginCom'
export default { export default {
name: 'Login', name: 'Login',
components: { PluginCom },
data() { data() {
// const validateUsername = (rule, value, callback) => {
// const userName = value.trim()
// validateUserName({ userName: userName }).then(res => {
// if (res.data) {
// callback()
// } else {
// callback(this.$t('login.username_error'))
// }
// }).catch(() => {
// callback(this.$t('login.username_error'))
// })
// // if (!validUsername(value)) {
// // callback(new Error('Please enter the correct user name'))
// // } else {
// // callback()
// // }
// }
// const validatePassword = (rule, value, callback) => {
// if (value.length < 8) {
// callback(this.$t('login.password_error'))
// } else {
// callback()
// }
// }
return { return {
loginForm: { loginForm: {
loginType: 0, loginType: 0,
...@@ -108,7 +87,7 @@ export default { ...@@ -108,7 +87,7 @@ export default {
loginImageUrl: null, loginImageUrl: null,
loginLogoUrl: null, loginLogoUrl: null,
axiosFinished: false, axiosFinished: false,
openLdap: false loginTypes: [0]
} }
}, },
computed: { computed: {
...@@ -126,7 +105,15 @@ export default { ...@@ -126,7 +105,15 @@ export default {
}, },
beforeCreate() { beforeCreate() {
ldapStatus().then(res => { ldapStatus().then(res => {
this.openLdap = res.success && res.data if (res.success && res.data) {
this.loginTypes.push(1)
}
})
oidcStatus().then(res => {
if (res.success && res.data) {
this.loginTypes.push(2)
}
}) })
}, },
created() { created() {
...@@ -169,6 +156,12 @@ export default { ...@@ -169,6 +156,12 @@ export default {
return false return false
} }
}) })
},
changeLoginType(val) {
if (val !== 2) return
this.$nextTick(() => {
})
} }
} }
} }
......
<template>
<div>
<async-component v-if="showAsync" :url="url" @execute-axios="executeAxios" @on-add-languanges="addLanguages" @plugin-call-back="pluginCallBack" />
<div v-else>
<h1>未知组件无法展示</h1>
</div>
</div>
</template>
<script>
import AsyncComponent from '@/components/AsyncComponent'
import i18n from '@/lang'
import bus from '@/utils/bus'
import { execute } from '@/api/system/dynamic'
export default {
name: 'PluginCom',
components: {
AsyncComponent
},
props: {
componentName: {
type: String,
default: null
}
},
data() {
return {
showAsync: false,
baseUrl: '/api/pluginCommon/component/',
url: null
}
},
created() {
if (this.componentName) {
this.showAsync = true
this.url = this.baseUrl + this.componentName
} else {
this.showAsync = false
}
},
methods: {
// hasLicense
executeAxios(options) {
execute(options).then(res => {
if (options.callBack) {
options.callBack(res)
}
}).catch(e => {
if (options.callBack) {
options.callBack(e)
}
})
},
addLanguages(options) {
for (const key in i18n.messages) {
if (Object.hasOwnProperty.call(i18n.messages, key)) {
const element = options[key]
i18n.mergeLocaleMessage(key, element)
}
}
},
pluginCallBack(param) {
const { eventName, eventParam } = param
bus.$emit(eventName, eventParam)
}
}
}
</script>
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
<!-- <el-table-column prop="gender" :label="$t('commons.gender')" width="60" /> --> <!-- <el-table-column prop="gender" :label="$t('commons.gender')" width="60" /> -->
<el-table-column prop="from" :label="$t('user.source')" width="80"> <el-table-column prop="from" :label="$t('user.source')" width="80">
<template slot-scope="scope"> <template slot-scope="scope">
<div>{{ scope.row.from === 0 ? 'LOCAL' : 'LDAP' }}</div> <div>{{ scope.row.from === 0 ? 'LOCAL' : scope.row.from === 1 ? 'LDAP' : 'OIDC' }}</div>
</template> </template>
</el-table-column> </el-table-column>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论