From b66c3765ef31d7b5415913d04170e9528a1b1caa Mon Sep 17 00:00:00 2001 From: shiziyuan9527 Date: Mon, 22 Jun 2020 14:13:34 +0800 Subject: [PATCH] ldap --- backend/pom.xml | 6 ++ .../java/io/metersphere/ldap/LdapService.java | 54 ++++++++++++ .../java/io/metersphere/ldap/PersonRepo.java | 11 +++ .../io/metersphere/ldap/PersonRepoImpl.java | 84 +++++++++++++++++++ .../ldap/controller/LdapController.java | 63 ++++++++++++++ .../io/metersphere/ldap/domain/Person.java | 22 +++++ .../io/metersphere/service/UserService.java | 59 ++++++++++++- .../settings/personal/PersonSetting.vue | 11 +++ frontend/src/login/Login.vue | 58 +++++++++---- 9 files changed, 351 insertions(+), 17 deletions(-) create mode 100644 backend/src/main/java/io/metersphere/ldap/LdapService.java create mode 100644 backend/src/main/java/io/metersphere/ldap/PersonRepo.java create mode 100644 backend/src/main/java/io/metersphere/ldap/PersonRepoImpl.java create mode 100644 backend/src/main/java/io/metersphere/ldap/controller/LdapController.java create mode 100644 backend/src/main/java/io/metersphere/ldap/domain/Person.java diff --git a/backend/pom.xml b/backend/pom.xml index 3088f0dedf..1bbe2a3d9e 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -153,6 +153,12 @@ 2.1.7 + + + org.springframework.boot + spring-boot-starter-data-ldap + + diff --git a/backend/src/main/java/io/metersphere/ldap/LdapService.java b/backend/src/main/java/io/metersphere/ldap/LdapService.java new file mode 100644 index 0000000000..65f6fcfad7 --- /dev/null +++ b/backend/src/main/java/io/metersphere/ldap/LdapService.java @@ -0,0 +1,54 @@ +package io.metersphere.ldap; + +import io.metersphere.commons.exception.MSException; +import io.metersphere.controller.request.LoginRequest; +import org.apache.shiro.realm.ldap.LdapUtils; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.naming.directory.DirContext; +import javax.naming.ldap.LdapContext; + +import java.util.List; + +import static org.springframework.ldap.query.LdapQueryBuilder.query; + +@Service +public class LdapService { + + @Resource + private LdapTemplate ldapTemplate; + + @Resource + private PersonRepoImpl personRepo; + + public boolean authenticate(LoginRequest request) { +// String userDn, String credentials + String username = request.getUsername(); + String credentials = request.getPassword(); + + List user = personRepo.findByName(username); + if (user.size() > 0) { + System.out.println(user); + } else { + MSException.throwException("no such user"); + } + try { + ldapTemplate.authenticate(query() + .where("objectclass").is("person") + .and("cn").is(username), credentials); + // Take care here - if a base was specified on the ContextSource + // that needs to be removed from the user DN for the lookup to succeed. + // ctx.lookup(userDn); + return true; + } catch (Exception e) { + // Context creation failed - authentication did not succeed + System.out.println("Login failed: " + e); + return false; + } finally { + // It is imperative that the created DirContext instance is always closed +// LdapUtils.closeContext((LdapContext) ctx); + } + } +} diff --git a/backend/src/main/java/io/metersphere/ldap/PersonRepo.java b/backend/src/main/java/io/metersphere/ldap/PersonRepo.java new file mode 100644 index 0000000000..9743d5884f --- /dev/null +++ b/backend/src/main/java/io/metersphere/ldap/PersonRepo.java @@ -0,0 +1,11 @@ +package io.metersphere.ldap; + + +import java.util.List; + +public interface PersonRepo { + + List getAllPersonNames(); + + List findByName(String name); +} diff --git a/backend/src/main/java/io/metersphere/ldap/PersonRepoImpl.java b/backend/src/main/java/io/metersphere/ldap/PersonRepoImpl.java new file mode 100644 index 0000000000..0df60df003 --- /dev/null +++ b/backend/src/main/java/io/metersphere/ldap/PersonRepoImpl.java @@ -0,0 +1,84 @@ +package io.metersphere.ldap; + + +import io.metersphere.ldap.domain.Person; +import org.apache.shiro.realm.ldap.LdapUtils; +import org.springframework.ldap.NamingException; +import org.springframework.ldap.core.*; +import org.springframework.ldap.core.support.AbstractContextMapper; +import org.springframework.ldap.query.LdapQuery; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.ldap.LdapContext; + + +import java.util.List; + +import static org.springframework.ldap.query.LdapQueryBuilder.query; + +@Service +public class PersonRepoImpl implements PersonRepo { + + @Resource + private LdapTemplate ldapTemplate; + + + @Override + public List getAllPersonNames() { + ldapTemplate.setIgnorePartialResultException(true); + return ldapTemplate.search( + query().where("objectclass").is("person"), + new AttributesMapper() { + @Override + public String mapFromAttributes(Attributes attrs) + throws NamingException, javax.naming.NamingException { + return attrs.toString(); + } + }); + } + + @Override + public List findByName(String name) { + ldapTemplate.setIgnorePartialResultException(true); + LdapQuery query = query() + .where("objectclass").is("person") + .and("cn").is(name); + return ldapTemplate.search(query, getContextMapper()); + } + + protected ContextMapper getContextMapper() { + return new PersonContextMapper(); + } + + + private static class PersonContextMapper extends AbstractContextMapper { + @Override + public Person doMapFromContext(DirContextOperations context) { + Person person = new Person(); + person.setCommonName(context.getStringAttribute("cn")); + person.setSuerName(context.getStringAttribute("sn")); + return person; + } + } + +// public boolean authenticate(String userDn, String credentials) { +// DirContext ctx = null; +// try { +// ctx = ldapTemplate.getContextSource().getContext(userDn, credentials); +// // Take care here - if a base was specified on the ContextSource +// // that needs to be removed from the user DN for the lookup to succeed. +//// ctx.lookup(userDn); +// return true; +// } catch (Exception e) { +// // Context creation failed - authentication did not succeed +// System.out.println("Login failed: " + e); +// return false; +// } finally { +// // It is imperative that the created DirContext instance is always closed +// LdapUtils.closeContext((LdapContext) ctx); +// } +// } +} diff --git a/backend/src/main/java/io/metersphere/ldap/controller/LdapController.java b/backend/src/main/java/io/metersphere/ldap/controller/LdapController.java new file mode 100644 index 0000000000..a7dc264364 --- /dev/null +++ b/backend/src/main/java/io/metersphere/ldap/controller/LdapController.java @@ -0,0 +1,63 @@ +package io.metersphere.ldap.controller; + +import io.metersphere.base.domain.UserRole; +import io.metersphere.controller.ResultHolder; +import io.metersphere.controller.request.LoginRequest; +import io.metersphere.dto.UserDTO; +import io.metersphere.i18n.Translator; +import io.metersphere.ldap.LdapService; +import io.metersphere.ldap.PersonRepoImpl; +import io.metersphere.service.UserService; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authc.*; +import org.apache.shiro.authz.UnauthorizedException; +import org.apache.shiro.subject.Subject; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; +import java.util.stream.Collectors; + +import static io.metersphere.commons.constants.SessionConstants.ATTR_USER; + +@RestController +@RequestMapping("/ldap") +public class LdapController { + + @Resource + private PersonRepoImpl personRepo; + @Resource + private UserService userService; + @Resource + private LdapService ldapService; + + @GetMapping("/test") + public List test() { + return personRepo.getAllPersonNames(); + } + + @GetMapping("/find/{name}") + public List test(@PathVariable String name) { + return personRepo.findByName(name); + } + + @GetMapping("/testUser") + public void testUser() { + // TODO LDAP 认证 +// personRepo.authenticate("Administrator@fit2cloud.com", "Calong@2015"); + } + + @PostMapping(value = "/signin") + public ResultHolder login(@RequestBody LoginRequest request) { + // TODO 1. LDAP 认证 2. 认证之后, 重新登录系统, 3. 执行其它 + ldapService.authenticate(request); + + return userService.login(request); + } + + + + + +} diff --git a/backend/src/main/java/io/metersphere/ldap/domain/Person.java b/backend/src/main/java/io/metersphere/ldap/domain/Person.java new file mode 100644 index 0000000000..2dcb9eca49 --- /dev/null +++ b/backend/src/main/java/io/metersphere/ldap/domain/Person.java @@ -0,0 +1,22 @@ +package io.metersphere.ldap.domain; + +import lombok.Data; +import org.springframework.ldap.odm.annotations.Attribute; +import org.springframework.ldap.odm.annotations.DnAttribute; +import org.springframework.ldap.odm.annotations.Id; + +import javax.naming.Name; + +@Data +public class Person { + + @Id + private Name id; + @DnAttribute(value="uid",index = 3) + private String uid; + @Attribute(name = "cn") + private String commonName; + @Attribute(name = "sn") + private String suerName; + private String userPassword; +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/service/UserService.java b/backend/src/main/java/io/metersphere/service/UserService.java index 934b4bbe2a..9f0ccfc342 100644 --- a/backend/src/main/java/io/metersphere/service/UserService.java +++ b/backend/src/main/java/io/metersphere/service/UserService.java @@ -10,6 +10,8 @@ import io.metersphere.commons.exception.MSException; import io.metersphere.commons.user.SessionUser; import io.metersphere.commons.utils.CodingUtil; import io.metersphere.commons.utils.SessionUtils; +import io.metersphere.controller.ResultHolder; +import io.metersphere.controller.request.LoginRequest; import io.metersphere.controller.request.member.AddMemberRequest; import io.metersphere.controller.request.member.EditPassWordRequest; import io.metersphere.controller.request.member.QueryMemberRequest; @@ -20,17 +22,23 @@ import io.metersphere.dto.UserDTO; import io.metersphere.dto.UserRoleDTO; import io.metersphere.i18n.Translator; import org.apache.commons.lang3.StringUtils; -import org.apache.shiro.authc.DisabledAccountException; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authc.*; +import org.apache.shiro.authz.UnauthorizedException; +import org.apache.shiro.subject.Subject; import org.springframework.beans.BeanUtils; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; +import org.springframework.web.bind.annotation.RequestBody; import javax.annotation.Resource; import java.util.*; import java.util.stream.Collectors; +import static io.metersphere.commons.constants.SessionConstants.ATTR_USER; + @Service @Transactional(rollbackFor = Exception.class) public class UserService { @@ -460,4 +468,53 @@ public class UserService { public List getTestManagerAndTestUserList(QueryMemberRequest request) { return extUserRoleMapper.getTestManagerAndTestUserList(request); } + + public ResultHolder login(LoginRequest request) { + String msg; + String username = StringUtils.trim(request.getUsername()); + String password = StringUtils.trim(request.getPassword()); + if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { + return ResultHolder.error("user or password can't be null"); + } + + UsernamePasswordToken token = new UsernamePasswordToken(username, password); + Subject subject = SecurityUtils.getSubject(); + + try { + subject.login(token); + if (subject.isAuthenticated()) { + UserDTO user = (UserDTO) subject.getSession().getAttribute(ATTR_USER); + // 自动选中组织,工作空间 + if (StringUtils.isEmpty(user.getLastOrganizationId())) { + List userRoles = user.getUserRoles(); + List test = userRoles.stream().filter(ur -> ur.getRoleId().startsWith("test")).collect(Collectors.toList()); + List org = userRoles.stream().filter(ur -> ur.getRoleId().startsWith("org")).collect(Collectors.toList()); + if (test.size() > 0) { + String wsId = test.get(0).getSourceId(); + switchUserRole("workspace", wsId); + } else if (org.size() > 0) { + String orgId = org.get(0).getSourceId(); + switchUserRole("organization", orgId); + } + } + // 返回 userDTO + return ResultHolder.success(subject.getSession().getAttribute("user")); + } else { + return ResultHolder.error(Translator.get("login_fail")); + } + } catch (ExcessiveAttemptsException e) { + msg = Translator.get("excessive_attempts"); + } catch (LockedAccountException e) { + msg = Translator.get("user_locked"); + } catch (DisabledAccountException e) { + msg = Translator.get("user_has_been_disabled"); + } catch (ExpiredCredentialsException e) { + msg = Translator.get("user_expires"); + } catch (AuthenticationException e) { + msg = e.getMessage(); + } catch (UnauthorizedException e) { + msg = Translator.get("not_authorized") + e.getMessage(); + } + return ResultHolder.error(msg); + } } diff --git a/frontend/src/business/components/settings/personal/PersonSetting.vue b/frontend/src/business/components/settings/personal/PersonSetting.vue index 07050c3259..05b2cbbba2 100644 --- a/frontend/src/business/components/settings/personal/PersonSetting.vue +++ b/frontend/src/business/components/settings/personal/PersonSetting.vue @@ -154,6 +154,17 @@ this.form = Object.assign({}, row); }, editPassword(row) { + this.$get("ldap/test", res => { + console.log(res) + }) + + this.$get("ldap/find/admin", res => { + console.log(res) + }) + + this.$get("ldap/testUser", res => { + console.log(res) + }) this.editPasswordVisible = true; }, updateUser(updateUserForm) { diff --git a/frontend/src/login/Login.vue b/frontend/src/login/Login.vue index 8d2fdad0cd..dc3cc5a087 100644 --- a/frontend/src/login/Login.vue +++ b/frontend/src/login/Login.vue @@ -15,6 +15,12 @@ {{$t('commons.welcome')}}
+ + + LDAP + 普通登录 + + @@ -60,7 +66,8 @@ return { form: { username: '', - password: '' + password: '', + authenticate: 'normal' }, rules: { username: [ @@ -105,24 +112,43 @@ submit(form) { this.$refs[form].validate((valid) => { if (valid) { - this.$post("signin", this.form, response => { - saveLocalStorage(response); - let language = response.data.language; - - if (!language) { - this.$get("language", response => { - language = response.data; - localStorage.setItem(DEFAULT_LANGUAGE, language) - window.location.href = "/" - }) - } else { - window.location.href = "/" - } - }); + switch (this.form.authenticate) { + case "normal": + this.normalLogin(); + break; + case "ldap": + this.ldapLogin(); + break; + default: + this.normalLogin(); + } } else { return false; } }); + }, + normalLogin() { + this.$post("signin", this.form, response => { + saveLocalStorage(response); + this.getLanguage(response.data.language); + }); + }, + ldapLogin() { + this.$post("ldap/signin", this.form, response => { + saveLocalStorage(response); + this.getLanguage(response.data.language); + }); + }, + getLanguage(language) { + if (!language) { + this.$get("language", response => { + language = response.data; + localStorage.setItem(DEFAULT_LANGUAGE, language) + window.location.href = "/" + }) + } else { + window.location.href = "/" + } } } } @@ -174,7 +200,7 @@ } .form { - margin-top: 60px; + margin-top: 30px; padding: 0 40px; }