提交 8ae401e3 编写于 作者: E elonlo 提交者: dailidong

Refactor login verification process (#1543)

* Refactor login verification process

* Delete application-combined.properties

application-combined.properties doesn't needed
Co-authored-by: Ndailidong <dailidong66@gmail.com>
上级 7762a622
......@@ -18,8 +18,8 @@ package org.apache.dolphinscheduler.api.controller;
import org.apache.dolphinscheduler.api.enums.Status;
import org.apache.dolphinscheduler.api.security.Authenticator;
import org.apache.dolphinscheduler.api.service.SessionService;
import org.apache.dolphinscheduler.api.service.UsersService;
import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.common.Constants;
import org.apache.dolphinscheduler.common.utils.StringUtils;
......@@ -36,6 +36,8 @@ import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import static org.apache.dolphinscheduler.api.enums.Status.*;
/**
......@@ -55,7 +57,7 @@ public class LoginController extends BaseController {
private SessionService sessionService;
@Autowired
private UsersService userService;
private Authenticator authenticator;
/**
......@@ -94,27 +96,20 @@ public class LoginController extends BaseController {
}
// verify username and password
User user = userService.queryUser(userName, userPassword);
if (user == null) {
return error(Status.USER_NAME_PASSWD_ERROR.getCode(),Status.USER_NAME_PASSWD_ERROR.getMsg()
);
}
// create session
String sessionId = sessionService.createSession(user, ip);
if (sessionId == null) {
return error(Status.LOGIN_SESSION_FAILED.getCode(),
Status.LOGIN_SESSION_FAILED.getMsg()
);
Result<Map<String, String>> result = authenticator.authenticate(userName, userPassword, ip);
if (result.getCode() != Status.SUCCESS.getCode()) {
return result;
}
response.setStatus(HttpStatus.SC_OK);
response.addCookie(new Cookie(Constants.SESSION_ID, sessionId));
Map<String, String> cookieMap = result.getData();
for (Map.Entry<String, String> cookieEntry : cookieMap.entrySet()) {
Cookie cookie = new Cookie(cookieEntry.getKey(), cookieEntry.getValue());
cookie.setHttpOnly(true);
response.addCookie(cookie);
}
logger.info("sessionId : {}" , sessionId);
return success(LOGIN_SUCCESS.getMsg(), sessionId);
return result;
} catch (Exception e) {
logger.error(USER_LOGIN_FAILURE.getMsg(),e);
return error(USER_LOGIN_FAILURE.getCode(), USER_LOGIN_FAILURE.getMsg());
......
......@@ -16,9 +16,9 @@
*/
package org.apache.dolphinscheduler.api.interceptor;
import org.apache.dolphinscheduler.api.security.Authenticator;
import org.apache.dolphinscheduler.api.service.SessionService;
import org.apache.dolphinscheduler.common.Constants;
import org.apache.dolphinscheduler.dao.entity.Session;
import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.dolphinscheduler.dao.mapper.UserMapper;
import org.apache.commons.httpclient.HttpStatus;
......@@ -44,6 +44,9 @@ public class LoginHandlerInterceptor implements HandlerInterceptor {
@Autowired
private UserMapper userMapper;
@Autowired
private Authenticator authenticator;
/**
* Intercept the execution of a handler. Called after HandlerMapping determined
* an appropriate handler object, but before HandlerAdapter invokes the handler.
......@@ -68,17 +71,7 @@ public class LoginHandlerInterceptor implements HandlerInterceptor {
String token = request.getHeader("token");
User user = null;
if (StringUtils.isEmpty(token)){
Session session = sessionService.getSession(request);
if (session == null) {
response.setStatus(HttpStatus.SC_UNAUTHORIZED);
logger.info("session info is null ");
return false;
}
//get user object from session
user = userMapper.selectById(session.getUserId());
user = authenticator.getAuthUser(request);
// if user is null
if (user == null) {
response.setStatus(HttpStatus.SC_UNAUTHORIZED);
......
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.api.security;
import com.baomidou.mybatisplus.annotation.EnumValue;
/**
* authentication type
*/
public enum AuthenticationType {
PASSWORD(0, "verify via user name and password"),
;
AuthenticationType(int code, String desc) {
this.code = code;
this.desc = desc;
}
@EnumValue
private final int code;
private final String desc;
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.api.security;
import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.dao.entity.User;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
public interface Authenticator {
/**
* Verifying legality via username and password
* @param username user name
* @param password user password
* @param extra extra info
* @return result object
*/
Result<Map<String, String>> authenticate(String username, String password, String extra);
/**
* Get authenticated user
* @param request http servlet request
* @return user
*/
User getAuthUser(HttpServletRequest request);
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.api.security;
import org.apache.dolphinscheduler.api.enums.Status;
import org.apache.dolphinscheduler.api.service.SessionService;
import org.apache.dolphinscheduler.api.service.UsersService;
import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.common.Constants;
import org.apache.dolphinscheduler.dao.entity.Session;
import org.apache.dolphinscheduler.dao.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.Map;
public class PasswordAuthenticator implements Authenticator {
private static final Logger logger = LoggerFactory.getLogger(PasswordAuthenticator.class);
@Autowired
private UsersService userService;
@Autowired
private SessionService sessionService;
@Override
public Result<Map<String, String>> authenticate(String username, String password, String extra) {
Result<Map<String, String>> result = new Result<>();
// verify username and password
User user = userService.queryUser(username, password);
if (user == null) {
result.setCode(Status.USER_NAME_PASSWD_ERROR.getCode());
result.setMsg(Status.USER_NAME_PASSWD_ERROR.getMsg());
return result;
}
// create session
String sessionId = sessionService.createSession(user, extra);
if (sessionId == null) {
result.setCode(Status.LOGIN_SESSION_FAILED.getCode());
result.setMsg(Status.LOGIN_SESSION_FAILED.getMsg());
return result;
}
logger.info("sessionId : {}" , sessionId);
result.setData(Collections.singletonMap(Constants.SESSION_ID, sessionId));
result.setCode(Status.SUCCESS.getCode());
result.setMsg(Status.LOGIN_SUCCESS.getMsg());
return result;
}
@Override
public User getAuthUser(HttpServletRequest request) {
Session session = sessionService.getSession(request);
if (session == null) {
logger.info("session info is null ");
return null;
}
//get user object from session
return userService.queryUser(session.getUserId());
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.api.security;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SecurityConfig {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
@Value("${security.authentication.type:PASSWORD}")
private String type;
private AutowireCapableBeanFactory beanFactory;
private AuthenticationType authenticationType;
@Autowired
public SecurityConfig(AutowireCapableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
private void setAuthenticationType(String type) {
if (StringUtils.isBlank(type)) {
logger.info("security.authentication.type configuration is empty, the default value 'PASSWORD'");
this.authenticationType = AuthenticationType.PASSWORD;
return;
}
this.authenticationType = AuthenticationType.valueOf(type);
}
@Bean(name = "authenticator")
public Authenticator authenticator() {
setAuthenticationType(type);
Authenticator authenticator;
switch (authenticationType) {
case PASSWORD:
authenticator = new PasswordAuthenticator();
break;
default:
throw new IllegalStateException("Unexpected value: " + authenticationType);
}
beanFactory.autowireBean(authenticator);
return authenticator;
}
}
......@@ -301,6 +301,25 @@ public class TenantService extends BaseService{
return result;
}
/**
* query tenant list via tenant code
* @param tenantCode tenant code
* @return tenant list
*/
public Map<String, Object> queryTenantList(String tenantCode) {
Map<String, Object> result = new HashMap<>(5);
List<Tenant> resourceList = tenantMapper.queryByTenantCode(tenantCode);
if (resourceList != null && resourceList.size() > 0) {
result.put(Constants.DATA_LIST, resourceList);
putMsg(result, Status.SUCCESS);
} else {
putMsg(result, Status.TENANT_NOT_EXIST);
}
return result;
}
/**
* verify tenant code
*
......
......@@ -114,6 +114,31 @@ public class UsersService extends BaseService {
return result;
}
User user = createUser(userName, userPassword, email, tenantId, phone, queue);
Tenant tenant = tenantMapper.queryById(tenantId);
// resource upload startup
if (PropertyUtils.getResUploadStartupState()){
// if tenant not exists
if (!HadoopUtils.getInstance().exists(HadoopUtils.getHdfsTenantDir(tenant.getTenantCode()))){
createTenantDirIfNotExists(tenant.getTenantCode());
}
String userPath = HadoopUtils.getHdfsUserDir(tenant.getTenantCode(),user.getId());
HadoopUtils.getInstance().mkdir(userPath);
}
putMsg(result, Status.SUCCESS);
return result;
}
@Transactional(rollbackFor = Exception.class)
public User createUser(String userName,
String userPassword,
String email,
int tenantId,
String phone,
String queue) throws Exception {
User user = new User();
Date now = new Date();
......@@ -133,21 +158,25 @@ public class UsersService extends BaseService {
// save user
userMapper.insert(user);
return user;
}
Tenant tenant = tenantMapper.queryById(tenantId);
// resource upload startup
if (PropertyUtils.getResUploadStartupState()){
// if tenant not exists
if (!HadoopUtils.getInstance().exists(HadoopUtils.getHdfsTenantDir(tenant.getTenantCode()))){
createTenantDirIfNotExists(tenant.getTenantCode());
}
String userPath = HadoopUtils.getHdfsUserDir(tenant.getTenantCode(),user.getId());
HadoopUtils.getInstance().mkdir(userPath);
}
putMsg(result, Status.SUCCESS);
return result;
/**
* query user by id
* @param id id
* @return user info
*/
public User queryUser(int id) {
return userMapper.selectById(id);
}
/**
* query user
* @param name name
* @return user info
*/
public User queryUser(String name) {
return userMapper.queryByUserNameAccurately(name);
}
/**
......
......@@ -37,4 +37,9 @@ spring.messages.encoding=UTF-8
#i18n classpath folder , file prefix messages, if have many files, use "," seperator
spring.messages.basename=i18n/messages
# Authentication types (supported types: PASSWORD)
security.authentication.type=PASSWORD
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.api.interceptor;
import org.apache.dolphinscheduler.api.ApiApplicationServer;
import org.apache.dolphinscheduler.api.security.Authenticator;
import org.apache.dolphinscheduler.common.enums.UserType;
import org.apache.dolphinscheduler.dao.entity.User;
import org.apache.dolphinscheduler.dao.mapper.UserMapper;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static org.mockito.Mockito.when;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ApiApplicationServer.class)
public class LoginHandlerInterceptorTest {
private static final Logger logger = LoggerFactory.getLogger(LoginHandlerInterceptorTest.class);
@Autowired
LoginHandlerInterceptor interceptor;
@MockBean
private Authenticator authenticator;
@MockBean
private UserMapper userMapper;
@Test
public void testPreHandle() {
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
// test no token and no cookie
Assert.assertFalse(interceptor.preHandle(request, response, null));
User mockUser = new User();
mockUser.setId(1);
mockUser.setUserType(UserType.GENERAL_USER);
// test no token
when(authenticator.getAuthUser(request)).thenReturn(mockUser);
Assert.assertTrue(interceptor.preHandle(request, response, null));
// test token
String token = "123456";
when(request.getHeader("token")).thenReturn(token);
when(userMapper.queryUserByToken(token)).thenReturn(mockUser);
Assert.assertTrue(interceptor.preHandle(request, response, null));
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.api.security;
import org.apache.dolphinscheduler.api.ApiApplicationServer;
import org.apache.dolphinscheduler.api.enums.Status;
import org.apache.dolphinscheduler.api.service.SessionService;
import org.apache.dolphinscheduler.api.service.UsersService;
import org.apache.dolphinscheduler.api.utils.Result;
import org.apache.dolphinscheduler.dao.entity.Session;
import org.apache.dolphinscheduler.dao.entity.User;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import static org.mockito.Mockito.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.UUID;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ApiApplicationServer.class)
public class PasswordAuthenticatorTest {
private static Logger logger = LoggerFactory.getLogger(PasswordAuthenticatorTest.class);
@Autowired
private AutowireCapableBeanFactory beanFactory;
@MockBean
private SessionService sessionService;
@MockBean
private UsersService usersService;
private PasswordAuthenticator authenticator;
private User mockUser;
private Session mockSession;
@Before
public void setUp() throws Exception {
authenticator = new PasswordAuthenticator();
beanFactory.autowireBean(authenticator);
mockUser = new User();
mockUser.setUserName("test");
mockUser.setEmail("test@test.com");
mockUser.setUserPassword("test");
mockUser.setId(1);
mockSession = new Session();
mockSession.setId(UUID.randomUUID().toString());
mockSession.setIp("127.0.0.1");
mockSession.setUserId(1);
mockSession.setLastLoginTime(new Date());
}
@Test
public void testAuthenticate() {
when(usersService.queryUser("test", "test")).thenReturn(mockUser);
when(sessionService.createSession(mockUser, "127.0.0.1")).thenReturn(mockSession.getId());
Result result = authenticator.authenticate("test", "test", "127.0.0.1");
Assert.assertEquals(Status.SUCCESS.getCode(), (int) result.getCode());
logger.info(result.toString());
}
@Test
public void testGetAuthUser() {
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
when(usersService.queryUser(mockUser.getId())).thenReturn(mockUser);
when(sessionService.getSession(request)).thenReturn(mockSession);
User user = authenticator.getAuthUser(request);
Assert.assertNotNull(user);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dolphinscheduler.api.security;
import org.apache.dolphinscheduler.api.ApiApplicationServer;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ApiApplicationServer.class)
@TestPropertySource(properties = {
"security.authentication.type=PASSWORD",
})
public class SecurityConfigTest {
@Autowired
private SecurityConfig securityConfig;
@Test
public void testAuthenticator() {
Authenticator authenticator = securityConfig.authenticator();
Assert.assertNotNull(authenticator);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册