@@ -2,7 +2,6 @@ package com.ningdatech.pmapi.common.handler; | |||
import cn.hutool.json.JSONUtil; | |||
import com.ningdatech.basic.model.ApiResponse; | |||
import com.ningdatech.pmapi.common.util.BizUtils; | |||
import org.springframework.core.MethodParameter; | |||
import org.springframework.http.MediaType; | |||
import org.springframework.http.converter.HttpMessageConverter; | |||
@@ -24,7 +23,7 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; | |||
"com.ningdatech.pmapi.projectlib.controller", | |||
"com.ningdatech.pmapi.sys.controller", | |||
"com.ningdatech.pmapi.todocenter.controller", | |||
"com.ningdatech.pmapi.user.controller" | |||
"com.ningdatech.pmapi.user.controller", | |||
}) | |||
public class GlobalResponseHandler implements ResponseBodyAdvice<Object> { | |||
@@ -20,6 +20,7 @@ import lombok.RequiredArgsConstructor; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.springframework.beans.BeanUtils; | |||
import org.springframework.stereotype.Component; | |||
import java.time.LocalDateTime; | |||
import java.util.Collections; | |||
import java.util.Map; | |||
@@ -69,7 +70,7 @@ public class ConstructionPlanManage { | |||
//首先要判断 项目当前状态 是不是 方案待申报 | |||
VUtils.isTrue(!ProjectStatusEnum.PLAN_TO_BE_DECLARED.getCode().equals(projectInfo.getStatus()) || | |||
!ProjectStatusEnum.NOT_APPROVED.getCode().equals(projectInfo.getStage())) | |||
!ProjectStatusEnum.NOT_APPROVED.getCode().equals(projectInfo.getStage())) | |||
.throwMessage("提交失败 该项目不是 待预审状态或者未立项阶段"); | |||
//TODO 再判断 该项目是否 真实走完 单位内部审批 | |||
@@ -145,7 +145,7 @@ public class AnnualPlanLibManage { | |||
try (InputStream inputStream = file.getInputStream()) { | |||
EasyExcel.read(inputStream, new AnalysisEventListener<AnnualLibImportDTO>() { | |||
private List<Project> records = new ArrayList<>(); | |||
private final List<Project> records = new ArrayList<>(); | |||
@Override | |||
public void onException(Exception exception, AnalysisContext context) throws Exception { | |||
@@ -179,7 +179,7 @@ public class AnnualPlanLibManage { | |||
project.setBeginTime(dataArr[0].trim()); | |||
project.setEndTime(dataArr[1].trim()); | |||
project.setProjectIntroduction(data.getProjectIntroduction()); | |||
project.setIsFirst(data.getIsFirst().equals("新建") ? 1 : 0); | |||
project.setIsFirst("新建".equals(data.getIsFirst()) ? 1 : 0); | |||
records.add(project); | |||
} | |||
@@ -2,9 +2,11 @@ package com.ningdatech.pmapi.sys.model.enumeration; | |||
import lombok.Getter; | |||
import java.util.Arrays; | |||
/** | |||
* <p> | |||
* DataScopeEnum | |||
* 数据权限可见范围枚举 | |||
* </p> | |||
* | |||
* @author WendyYang | |||
@@ -28,4 +30,23 @@ public enum DataScopeEnum { | |||
this.code = code; | |||
this.desc = desc; | |||
} | |||
public boolean eq(Integer code) { | |||
return this.code.equals(code); | |||
} | |||
/** | |||
* 根据code获取枚举实例 | |||
* | |||
* @param code 编码 | |||
* @return {@link DataScopeEnum} | |||
* @author WendyYang | |||
**/ | |||
public static DataScopeEnum getByCode(Integer code) { | |||
return Arrays.stream(values()) | |||
.filter(w -> w.eq(code)) | |||
.findFirst() | |||
.orElseThrow(() -> new IllegalArgumentException("无效的数据权限可见范围编码")); | |||
} | |||
} |
@@ -0,0 +1,30 @@ | |||
package com.ningdatech.pmapi.user.constant; | |||
import io.swagger.annotations.ApiModel; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Getter; | |||
/** | |||
* @author liuxinxin | |||
* @date 2022/8/17 下午5:55 | |||
*/ | |||
@AllArgsConstructor | |||
@Getter | |||
@ApiModel("登陆类型") | |||
public enum LoginTypeEnum { | |||
/** | |||
* 浙政钉扫码登陆 | |||
*/ | |||
DING_QR_LOGIN, | |||
/** | |||
* 手机号验证码登陆 | |||
*/ | |||
PHONE_VERIFICATION_CODE_LOGIN, | |||
/** | |||
* 账号密码登陆 | |||
*/ | |||
USERNAME_PASSWORD_LOGIN; | |||
} |
@@ -1,20 +0,0 @@ | |||
package com.ningdatech.pmapi.user.controller; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
import org.springframework.stereotype.Controller; | |||
/** | |||
* <p> | |||
* 前端控制器 | |||
* </p> | |||
* | |||
* @author Lierbao | |||
* @since 2023-02-01 | |||
*/ | |||
@Controller | |||
@RequestMapping("/pmapi.user/nd-user-auth") | |||
public class NdUserAuthController { | |||
} |
@@ -1,20 +0,0 @@ | |||
package com.ningdatech.pmapi.user.controller; | |||
import org.springframework.stereotype.Controller; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
/** | |||
* <p> | |||
* 前端控制器 | |||
* </p> | |||
* | |||
* @author Lierbao | |||
* @since 2023-02-01 | |||
*/ | |||
@Controller | |||
@RequestMapping("/pmapi.user/nd-user-info") | |||
public class NdUserInfoController { | |||
} |
@@ -36,13 +36,16 @@ public class UserAuthController { | |||
private final ObjectMapper objectMapper; | |||
@PostMapping(value = "/login/password", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) | |||
@ApiOperation(value = "账号密码的登陆方式") | |||
@PostMapping(value = "/login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) | |||
@ApiOperation(value = "登陆") | |||
@ApiImplicitParams({ | |||
@ApiImplicitParam(name = "username", value = "用户名", required = true, paramType = "form", dataType = "String"), | |||
@ApiImplicitParam(name = "password", value = "密码", required = true, paramType = "form", dataType = "String")}) | |||
public void loginByUsernameAndPassword(@RequestParam("username") String username, | |||
@RequestParam("password") String password) { | |||
@ApiImplicitParam(name = "identifier", value = "账号", required = true, paramType = "form", dataType = "String"), | |||
@ApiImplicitParam(name = "credential", value = "凭证", required = true, paramType = "form", dataType = "String"), | |||
@ApiImplicitParam(name = "loginType", value = "DING_QR_LOGIN 浙政钉扫码登陆,PHONE_VERIFICATION_CODE_LOGIN 手机号验证码登陆" | |||
, required = true, paramType = "form", dataType = "String")}) | |||
public void loginByUsernameAndPassword(@RequestParam(value = "identifier",required = false) String identifier, | |||
@RequestParam(value = "credential",required = false) String credential, | |||
@RequestParam("loginType") String loginType) { | |||
// 不实现任何内容,只是为了出api文档 | |||
} | |||
@@ -18,6 +18,12 @@ public class UserAuthLoginManage { | |||
private final IUserAuthService iUserAuthService; | |||
private final IUserInfoService iUserInfoService; | |||
/** | |||
* 根据用户名获取 | |||
* | |||
* @param username | |||
* @return | |||
*/ | |||
public UserFullInfoDTO queryUserInfoInPasswordAuth(String username) { | |||
UserFullInfoDTO userFullInfoDTO = new UserFullInfoDTO(); | |||
userFullInfoDTO.setCompanyId(1L); | |||
@@ -27,4 +33,36 @@ public class UserAuthLoginManage { | |||
userFullInfoDTO.setUsername("测试账号"); | |||
return userFullInfoDTO; | |||
} | |||
/** | |||
* 根据手机号获取 | |||
* | |||
* @param phoneNo | |||
* @return | |||
*/ | |||
public UserFullInfoDTO queryUserInfoInPhoneNoAuth(String phoneNo) { | |||
UserFullInfoDTO userFullInfoDTO = new UserFullInfoDTO(); | |||
userFullInfoDTO.setCompanyId(1L); | |||
userFullInfoDTO.setUserId(1L); | |||
userFullInfoDTO.setIdentifier("123456"); | |||
userFullInfoDTO.setRealName("测试账号"); | |||
userFullInfoDTO.setUsername("测试账号"); | |||
return userFullInfoDTO; | |||
} | |||
/** | |||
* 根据accountId | |||
* | |||
* @param accountId | |||
* @return | |||
*/ | |||
public UserFullInfoDTO queryUserInfoInAccountIdAuth(String accountId) { | |||
UserFullInfoDTO userFullInfoDTO = new UserFullInfoDTO(); | |||
userFullInfoDTO.setCompanyId(1L); | |||
userFullInfoDTO.setUserId(1L); | |||
userFullInfoDTO.setIdentifier("123456"); | |||
userFullInfoDTO.setRealName("测试账号"); | |||
userFullInfoDTO.setUsername("测试账号"); | |||
return userFullInfoDTO; | |||
} | |||
} |
@@ -1,13 +1,11 @@ | |||
package com.ningdatech.pmapi.user.security.auth; | |||
import com.google.common.collect.Lists; | |||
import com.ningdatech.basic.util.CollUtils; | |||
import com.ningdatech.basic.util.NdJsonUtil; | |||
import com.ningdatech.basic.util.StrPool; | |||
import com.ningdatech.pmapi.common.constant.BizConst; | |||
import com.ningdatech.pmapi.common.constant.CommonConstant; | |||
import com.ningdatech.pmapi.user.security.auth.handler.DefaultExpiredSessionStrategy; | |||
import com.ningdatech.pmapi.user.security.auth.password.UsernamePasswordAuthSecurityConfig; | |||
import com.ningdatech.pmapi.user.security.auth.credential.CredentialAuthSecurityConfig; | |||
import org.springframework.beans.factory.annotation.Qualifier; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.http.HttpStatus; | |||
@@ -18,7 +16,6 @@ import org.springframework.security.web.authentication.logout.LogoutSuccessHandl | |||
import org.springframework.security.web.csrf.CookieCsrfTokenRepository; | |||
import java.io.PrintWriter; | |||
import java.util.ArrayList; | |||
import java.util.Map; | |||
import java.util.Set; | |||
@@ -31,16 +28,16 @@ import java.util.Set; | |||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { | |||
private final AuthProperties authProperties; | |||
private final UsernamePasswordAuthSecurityConfig usernamePasswordAuthSecurityConfig; | |||
private final CredentialAuthSecurityConfig credentialAuthSecurityConfig; | |||
private final LogoutSuccessHandler logoutSuccessHandler; | |||
private final DefaultExpiredSessionStrategy defaultExpiredSessionStrategy; | |||
public WebSecurityConfig(AuthProperties authProperties, | |||
UsernamePasswordAuthSecurityConfig usernamePasswordAuthSecurityConfig, | |||
CredentialAuthSecurityConfig credentialAuthSecurityConfig, | |||
@Qualifier(value = "defaultLogoutSuccessHandler") LogoutSuccessHandler logoutSuccessHandler, | |||
DefaultExpiredSessionStrategy defaultExpiredSessionStrategy) { | |||
this.authProperties = authProperties; | |||
this.usernamePasswordAuthSecurityConfig = usernamePasswordAuthSecurityConfig; | |||
this.credentialAuthSecurityConfig = credentialAuthSecurityConfig; | |||
this.logoutSuccessHandler = logoutSuccessHandler; | |||
this.defaultExpiredSessionStrategy = defaultExpiredSessionStrategy; | |||
} | |||
@@ -50,7 +47,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { | |||
assemblerPreAuthUrls(http); | |||
http.formLogin() | |||
.loginPage(authProperties.getAuthRequireUrl()) | |||
.and().apply(usernamePasswordAuthSecurityConfig) | |||
.and().apply(credentialAuthSecurityConfig) | |||
.and() | |||
.authorizeRequests().antMatchers(authProperties.getIgnoreAuthUrlsArray()).permitAll().anyRequest() | |||
.authenticated().and() | |||
@@ -0,0 +1,11 @@ | |||
package com.ningdatech.pmapi.user.security.auth.constants; | |||
/** | |||
* @author liuxinxin | |||
* @date 2023/2/14 上午11:29 | |||
*/ | |||
public class UserDeatilsServiceConstant { | |||
public static final String USER_DETAILS_SERVICE_SEPARATOR = "@###@"; | |||
} |
@@ -1,4 +1,4 @@ | |||
package com.ningdatech.pmapi.user.security.auth.password; | |||
package com.ningdatech.pmapi.user.security.auth.credential; | |||
import com.ningdatech.pmapi.user.manage.UserAuthLoginManage; | |||
@@ -16,17 +16,17 @@ import java.util.Objects; | |||
* @date 2022/9/30 上午9:49 | |||
*/ | |||
@Service("passwordLoginUserDetailService") | |||
@Service("accountIdLoginUserDetailService") | |||
@RequiredArgsConstructor | |||
public class PasswordLoginUserDetailService implements UserDetailsService { | |||
public class AccountIdLoginUserDetailService implements UserDetailsService { | |||
private final UserAuthLoginManage userAuthLoginManage; | |||
@Override | |||
public UserInfoDetails loadUserByUsername(String username) throws UsernameNotFoundException { | |||
UserFullInfoDTO userFullInfoDTO = userAuthLoginManage.queryUserInfoInPasswordAuth(username); | |||
public UserInfoDetails loadUserByUsername(String accountId) throws UsernameNotFoundException { | |||
UserFullInfoDTO userFullInfoDTO = userAuthLoginManage.queryUserInfoInAccountIdAuth(accountId); | |||
if (Objects.isNull(userFullInfoDTO)) { | |||
throw new UsernameNotFoundException(String.format("%s user not exist", username)); | |||
throw new UsernameNotFoundException(String.format("%s user not exist", accountId)); | |||
} | |||
UserInfoDetails userInfoDetails = new UserInfoDetails(); | |||
userInfoDetails.setUserId(userFullInfoDTO.getUserId()); |
@@ -0,0 +1,108 @@ | |||
package com.ningdatech.pmapi.user.security.auth.credential; | |||
import com.ningdatech.basic.exception.BizException; | |||
import com.ningdatech.pmapi.user.constant.LoginTypeEnum; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.springframework.http.HttpMethod; | |||
import org.springframework.security.authentication.AuthenticationServiceException; | |||
import org.springframework.security.authentication.BadCredentialsException; | |||
import org.springframework.security.authentication.InternalAuthenticationServiceException; | |||
import org.springframework.security.core.Authentication; | |||
import org.springframework.security.core.AuthenticationException; | |||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; | |||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
/** | |||
* @Author LiuXinXin | |||
* @Date 2020/8/3 8:46 下午 | |||
* @Version 1.0 | |||
**/ | |||
public class CredentialAuthFilter extends AbstractAuthenticationProcessingFilter { | |||
private boolean postOnly = true; | |||
private static final String IDENTIFIER_PARAMETER = "identifier"; | |||
private static final String CREDENTIAL_PARAMETER = "credential"; | |||
private static final String LOGIN_TYPE_PARAMETER = "loginType"; | |||
// ~ Constructors | |||
// =================================================================================================== | |||
public CredentialAuthFilter(String processingUrl) { | |||
super(new AntPathRequestMatcher(processingUrl, HttpMethod.POST.name())); | |||
} | |||
// ~ Methods | |||
// ======================================================================================================== | |||
@Override | |||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) | |||
throws AuthenticationException { | |||
if (postOnly && !request.getMethod().equals(HttpMethod.POST.name())) { | |||
throw new AuthenticationServiceException("请求方法错误"); | |||
} | |||
String identifier = request.getParameter(IDENTIFIER_PARAMETER); | |||
String credential = request.getParameter(CREDENTIAL_PARAMETER); | |||
String loginType = request.getParameter(LOGIN_TYPE_PARAMETER); | |||
if (StringUtils.isBlank(loginType)) { | |||
throw new BadCredentialsException("登陆类型不能为空"); | |||
} | |||
paramValid(identifier, credential, loginType); | |||
identifier = trim(identifier); | |||
credential = trim(credential); | |||
loginType = trim(loginType); | |||
try { | |||
CredentialAuthToken authRequest = new CredentialAuthToken(identifier, credential, loginType); | |||
// Allow subclasses to set the "details" property | |||
setDetails(request, authRequest); | |||
return this.getAuthenticationManager().authenticate(authRequest); | |||
} catch (AuthenticationException e) { | |||
throw new BadCredentialsException("账号或密码错误"); | |||
} catch (BizException e) { | |||
throw new BadCredentialsException(e.getMessage()); | |||
} catch (Exception e) { | |||
throw new InternalAuthenticationServiceException("授权失败:", e); | |||
} | |||
} | |||
protected void setDetails(HttpServletRequest request, CredentialAuthToken authRequest) { | |||
authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); | |||
} | |||
private void paramValid(String identifier, String credential, String loginType) { | |||
LoginTypeEnum loginTypeEnum = LoginTypeEnum.valueOf(loginType); | |||
switch (loginTypeEnum) { | |||
case DING_QR_LOGIN: { | |||
if (StringUtils.isBlank(credential)) { | |||
throw new BadCredentialsException("浙政钉扫码登陆 授权码 不能为空 credential"); | |||
} | |||
} | |||
break; | |||
case USERNAME_PASSWORD_LOGIN: { | |||
if (StringUtils.isBlank(identifier) || StringUtils.isBlank(credential)) { | |||
throw new BadCredentialsException("账号密码登陆 账号密码不能为空 identifier credential"); | |||
} | |||
} | |||
break; | |||
case PHONE_VERIFICATION_CODE_LOGIN: { | |||
if (StringUtils.isBlank(identifier) || StringUtils.isBlank(credential)) { | |||
throw new BadCredentialsException("手机号验证码登陆 手机号或验证码不能为空 identifier credential"); | |||
} | |||
} | |||
break; | |||
} | |||
} | |||
private String trim(String trimStr) { | |||
if (StringUtils.isNotBlank(trimStr)) { | |||
return trimStr.trim(); | |||
} | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,103 @@ | |||
package com.ningdatech.pmapi.user.security.auth.credential; | |||
import com.ningdatech.basic.model.GenericResult; | |||
import com.ningdatech.pmapi.user.constant.LoginTypeEnum; | |||
import com.ningdatech.pmapi.user.security.auth.constants.UserDeatilsServiceConstant; | |||
import com.ningdatech.zwdd.client.ZwddAuthClient; | |||
import org.springframework.security.authentication.AuthenticationProvider; | |||
import org.springframework.security.authentication.BadCredentialsException; | |||
import org.springframework.security.authentication.InternalAuthenticationServiceException; | |||
import org.springframework.security.core.Authentication; | |||
import org.springframework.security.core.AuthenticationException; | |||
import org.springframework.security.core.userdetails.UserDetails; | |||
import org.springframework.security.core.userdetails.UserDetailsService; | |||
import org.springframework.security.crypto.password.PasswordEncoder; | |||
import java.util.Objects; | |||
/** | |||
* @Author LiuXinXin | |||
* @Date 2020/8/3 8:55 下午 | |||
* @Version 1.0 | |||
**/ | |||
public class CredentialAuthProvider implements AuthenticationProvider { | |||
private UserDetailsService userDetailsService; | |||
private PasswordEncoder passwordEncoder; | |||
private ZwddAuthClient zwddAuthClient; | |||
@Override | |||
public Authentication authenticate(Authentication authentication) throws AuthenticationException { | |||
if (!(authentication instanceof CredentialAuthToken)) { | |||
throw new RuntimeException("CustomAuthProvider 只支持 CustomAuthToken"); | |||
} | |||
CredentialAuthToken authenticationToken = (CredentialAuthToken) authentication; | |||
String principal = (String) authenticationToken.getPrincipal(); | |||
UserDetails user = null; | |||
LoginTypeEnum loginTypeEnum = authenticationToken.getLoginTypeEnum(); | |||
switch (loginTypeEnum) { | |||
case DING_QR_LOGIN: { | |||
String code = (String) authenticationToken.getCredentials(); | |||
GenericResult<String> accountResult = zwddAuthClient.getAccountId(code); | |||
if (!accountResult.isSuccess()) { | |||
throw new BadCredentialsException("login fail! 浙政钉校验失败"); | |||
} | |||
String accountId = accountResult.getData(); | |||
if (Objects.isNull(accountId)) { | |||
throw new BadCredentialsException("login fail! 浙政钉校验失败"); | |||
} | |||
user = userDetailsService.loadUserByUsername(accountId + UserDeatilsServiceConstant.USER_DETAILS_SERVICE_SEPARATOR + loginTypeEnum.name()); | |||
} | |||
break; | |||
case PHONE_VERIFICATION_CODE_LOGIN: { | |||
// TODO 校验短信验证码 | |||
user = userDetailsService.loadUserByUsername(principal + UserDeatilsServiceConstant.USER_DETAILS_SERVICE_SEPARATOR + loginTypeEnum.name()); | |||
} | |||
break; | |||
case USERNAME_PASSWORD_LOGIN: { | |||
user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal()); | |||
if (user == null) { | |||
throw new InternalAuthenticationServiceException("can not get user info!"); | |||
} | |||
// 账号密码登陆 更改 | |||
additionalAuthenticationChecks(user, authenticationToken); | |||
} | |||
break; | |||
} | |||
// 将用户定义的user放入token中,这样可以在session中查询到所有自定义的用户信息 | |||
return new CredentialAuthToken(user, user.getPassword(), user.getAuthorities()); | |||
} | |||
protected void additionalAuthenticationChecks(UserDetails userDetails, CredentialAuthToken authentication) | |||
throws AuthenticationException { | |||
if (authentication.getCredentials() == null) { | |||
throw new BadCredentialsException("login fail! password is null"); | |||
} | |||
String presentedPassword = authentication.getCredentials().toString(); | |||
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { | |||
throw new BadCredentialsException("login fail! password is error"); | |||
} | |||
} | |||
@Override | |||
public boolean supports(Class<?> authentication) { | |||
return CredentialAuthToken.class.isAssignableFrom(authentication); | |||
} | |||
public void setUserDetailsService(UserDetailsService userDetailsService) { | |||
this.userDetailsService = userDetailsService; | |||
} | |||
public void setPasswordEncoder(PasswordEncoder passwordEncoder) { | |||
this.passwordEncoder = passwordEncoder; | |||
} | |||
public void setZwddAuthClient(ZwddAuthClient zwddAuthClient) { | |||
this.zwddAuthClient = zwddAuthClient; | |||
} | |||
} |
@@ -1,6 +1,7 @@ | |||
package com.ningdatech.pmapi.user.security.auth.password; | |||
package com.ningdatech.pmapi.user.security.auth.credential; | |||
import com.ningdatech.pmapi.user.security.auth.AuthProperties; | |||
import com.ningdatech.zwdd.client.ZwddAuthClient; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.beans.factory.annotation.Qualifier; | |||
import org.springframework.security.authentication.AuthenticationManager; | |||
@@ -18,7 +19,7 @@ import org.springframework.stereotype.Component; | |||
* 账号密码登陆的认证配置 | |||
*/ | |||
@Component | |||
public class UsernamePasswordAuthSecurityConfig | |||
public class CredentialAuthSecurityConfig | |||
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> { | |||
@Autowired | |||
@@ -30,8 +31,8 @@ public class UsernamePasswordAuthSecurityConfig | |||
protected AuthenticationFailureHandler defaultLoginFailureHandler; | |||
@Autowired | |||
@Qualifier(value = "passwordLoginUserDetailService") | |||
private UserDetailsService passwordLoginUserDetailService; | |||
@Qualifier(value = "credentialLoginUserDetailService") | |||
private UserDetailsService credentialLoginUserDetailService; | |||
@Autowired | |||
private PasswordEncoder passwordEncoder; | |||
@@ -41,20 +42,26 @@ public class UsernamePasswordAuthSecurityConfig | |||
private AuthenticationManager authenticationManager; | |||
@Autowired | |||
private ZwddAuthClient zwddAuthClient; | |||
@Override | |||
public void configure(HttpSecurity http) throws Exception { | |||
UsernamePasswordAuthFilter usernamePasswordAuthFilter = | |||
new UsernamePasswordAuthFilter(authProperties.getPasswordLoginUrl()); | |||
CredentialAuthFilter credentialAuthFilter = | |||
new CredentialAuthFilter(authProperties.getPasswordLoginUrl()); | |||
authenticationManager = http.getSharedObject(AuthenticationManager.class); | |||
usernamePasswordAuthFilter.setAuthenticationManager(authenticationManager); | |||
usernamePasswordAuthFilter.setAuthenticationSuccessHandler(defaultLoginSuccessHandler); | |||
usernamePasswordAuthFilter.setAuthenticationFailureHandler(defaultLoginFailureHandler); | |||
credentialAuthFilter.setAuthenticationManager(authenticationManager); | |||
credentialAuthFilter.setAuthenticationSuccessHandler(defaultLoginSuccessHandler); | |||
credentialAuthFilter.setAuthenticationFailureHandler(defaultLoginFailureHandler); | |||
UsernamePasswordAuthProvider authenticationProvider = new UsernamePasswordAuthProvider(); | |||
authenticationProvider.setUserDetailsService(passwordLoginUserDetailService); | |||
CredentialAuthProvider authenticationProvider = new CredentialAuthProvider(); | |||
authenticationProvider.setUserDetailsService(credentialLoginUserDetailService); | |||
// 确保对密码进行加密的encoder和解密的encoder相同 | |||
authenticationProvider.setPasswordEncoder(passwordEncoder); | |||
http.authenticationProvider(authenticationProvider).addFilterAfter(usernamePasswordAuthFilter, | |||
// 传入浙政钉client | |||
authenticationProvider.setZwddAuthClient(zwddAuthClient); | |||
http.authenticationProvider(authenticationProvider).addFilterAfter(credentialAuthFilter, | |||
UsernamePasswordAuthenticationFilter.class); | |||
} | |||
@@ -1,5 +1,6 @@ | |||
package com.ningdatech.pmapi.user.security.auth.password; | |||
package com.ningdatech.pmapi.user.security.auth.credential; | |||
import com.ningdatech.pmapi.user.constant.LoginTypeEnum; | |||
import org.springframework.security.authentication.AbstractAuthenticationToken; | |||
import org.springframework.security.core.GrantedAuthority; | |||
import org.springframework.security.core.SpringSecurityCoreVersion; | |||
@@ -11,7 +12,7 @@ import java.util.Collection; | |||
* @Date 2020/8/3 8:52 下午 | |||
* @Version 1.0 | |||
**/ | |||
public class UsernamePasswordAuthToken extends AbstractAuthenticationToken { | |||
public class CredentialAuthToken extends AbstractAuthenticationToken { | |||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; | |||
@@ -19,16 +20,18 @@ public class UsernamePasswordAuthToken extends AbstractAuthenticationToken { | |||
private final Object credentials; | |||
private final LoginTypeEnum loginTypeEnum; | |||
/** | |||
* This constructor can be safely used by any code that wishes to create a | |||
* <code>UsernamePasswordAuthenticationToken</code>, as the {@link #isAuthenticated()} will return | |||
* <code>false</code>. | |||
*/ | |||
public UsernamePasswordAuthToken(String principal, String credentials) { | |||
public CredentialAuthToken(String principal, String credentials, String loginTypeEnum) { | |||
super(null); | |||
this.principal = principal; | |||
this.credentials = credentials; | |||
this.loginTypeEnum = LoginTypeEnum.valueOf(loginTypeEnum); | |||
setAuthenticated(false); | |||
} | |||
@@ -40,15 +43,20 @@ public class UsernamePasswordAuthToken extends AbstractAuthenticationToken { | |||
* @param principal | |||
* @param authorities | |||
*/ | |||
public UsernamePasswordAuthToken(Object principal, Object credentials, | |||
Collection<? extends GrantedAuthority> authorities) { | |||
public CredentialAuthToken(Object principal, Object credentials, | |||
Collection<? extends GrantedAuthority> authorities) { | |||
super(authorities); | |||
this.principal = principal; | |||
this.credentials = credentials; | |||
this.loginTypeEnum = null; | |||
// must use super, as we override | |||
super.setAuthenticated(true); | |||
} | |||
public LoginTypeEnum getLoginTypeEnum() { | |||
return this.loginTypeEnum; | |||
} | |||
@Override | |||
public Object getCredentials() { | |||
return this.credentials; |
@@ -0,0 +1,67 @@ | |||
package com.ningdatech.pmapi.user.security.auth.credential; | |||
import com.ningdatech.pmapi.user.constant.LoginTypeEnum; | |||
import com.ningdatech.pmapi.user.manage.UserAuthLoginManage; | |||
import com.ningdatech.pmapi.user.security.auth.constants.UserDeatilsServiceConstant; | |||
import com.ningdatech.pmapi.user.security.auth.model.UserFullInfoDTO; | |||
import com.ningdatech.pmapi.user.security.auth.model.UserInfoDetails; | |||
import lombok.RequiredArgsConstructor; | |||
import org.springframework.security.core.userdetails.UserDetailsService; | |||
import org.springframework.security.core.userdetails.UsernameNotFoundException; | |||
import org.springframework.stereotype.Service; | |||
import java.util.Objects; | |||
/** | |||
* @author LiuXinXin | |||
* @date 2022/9/30 上午9:49 | |||
*/ | |||
@Service("credentialLoginUserDetailService") | |||
@RequiredArgsConstructor | |||
public class CredentialLoginUserDetailService implements UserDetailsService { | |||
private final UserAuthLoginManage userAuthLoginManage; | |||
@Override | |||
public UserInfoDetails loadUserByUsername(String username) throws UsernameNotFoundException { | |||
String[] split = username.split(UserDeatilsServiceConstant.USER_DETAILS_SERVICE_SEPARATOR); | |||
username = split[0]; | |||
String loginTypeStr = split[1]; | |||
LoginTypeEnum loginTypeEnum = LoginTypeEnum.valueOf(loginTypeStr); | |||
UserFullInfoDTO userFullInfoDTO = null; | |||
switch (loginTypeEnum) { | |||
case PHONE_VERIFICATION_CODE_LOGIN: { | |||
userFullInfoDTO = userAuthLoginManage.queryUserInfoInPhoneNoAuth(username); | |||
} | |||
break; | |||
case USERNAME_PASSWORD_LOGIN: { | |||
userFullInfoDTO = userAuthLoginManage.queryUserInfoInPasswordAuth(username); | |||
} | |||
break; | |||
case DING_QR_LOGIN: { | |||
userFullInfoDTO = userAuthLoginManage.queryUserInfoInAccountIdAuth(username); | |||
} | |||
break; | |||
default: { | |||
throw new UsernameNotFoundException(String.format("%s user not exist", username)); | |||
} | |||
} | |||
if (Objects.isNull(userFullInfoDTO)) { | |||
throw new UsernameNotFoundException(String.format("%s user not exist", username)); | |||
} | |||
UserInfoDetails userInfoDetails = new UserInfoDetails(); | |||
userInfoDetails.setUserId(userFullInfoDTO.getUserId()); | |||
userInfoDetails.setUsername(userFullInfoDTO.getUsername()); | |||
userInfoDetails.setRealName(userFullInfoDTO.getRealName()); | |||
userInfoDetails.setRole(userFullInfoDTO.getRole()); | |||
userInfoDetails.setRegionCode(userFullInfoDTO.getRegionCode()); | |||
userInfoDetails.setCompanyId(userFullInfoDTO.getCompanyId()); | |||
userInfoDetails.setIdentifier(userFullInfoDTO.getIdentifier()); | |||
userInfoDetails.setPassword(userFullInfoDTO.getCredential()); | |||
return userInfoDetails; | |||
} | |||
} |
@@ -1,71 +0,0 @@ | |||
package com.ningdatech.pmapi.user.security.auth.password; | |||
import com.ningdatech.basic.exception.BizException; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.springframework.http.HttpMethod; | |||
import org.springframework.security.authentication.AuthenticationServiceException; | |||
import org.springframework.security.authentication.BadCredentialsException; | |||
import org.springframework.security.authentication.InternalAuthenticationServiceException; | |||
import org.springframework.security.core.Authentication; | |||
import org.springframework.security.core.AuthenticationException; | |||
import org.springframework.security.core.userdetails.UsernameNotFoundException; | |||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; | |||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
/** | |||
* @Author LiuXinXin | |||
* @Date 2020/8/3 8:46 下午 | |||
* @Version 1.0 | |||
**/ | |||
public class UsernamePasswordAuthFilter extends AbstractAuthenticationProcessingFilter { | |||
private boolean postOnly = true; | |||
private static final String USERNAME_PARAMETER = "username"; | |||
private static final String PASSWORD_PARAMETER = "password"; | |||
// ~ Constructors | |||
// =================================================================================================== | |||
public UsernamePasswordAuthFilter(String processingUrl) { | |||
super(new AntPathRequestMatcher(processingUrl, HttpMethod.POST.name())); | |||
} | |||
// ~ Methods | |||
// ======================================================================================================== | |||
@Override | |||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) | |||
throws AuthenticationException { | |||
if (postOnly && !request.getMethod().equals(HttpMethod.POST.name())) { | |||
throw new AuthenticationServiceException("请求方法错误"); | |||
} | |||
String username = request.getParameter(USERNAME_PARAMETER); | |||
String password = request.getParameter(PASSWORD_PARAMETER); | |||
if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { | |||
throw new UsernameNotFoundException("用户名或密码不能为空"); | |||
} | |||
username = username.trim(); | |||
password = password.trim(); | |||
try { | |||
UsernamePasswordAuthToken authRequest = new UsernamePasswordAuthToken(username, password); | |||
// Allow subclasses to set the "details" property | |||
setDetails(request, authRequest); | |||
return this.getAuthenticationManager().authenticate(authRequest); | |||
} catch (AuthenticationException e) { | |||
throw new BadCredentialsException("账号或密码错误"); | |||
} catch (BizException e) { | |||
throw new BadCredentialsException(e.getMessage()); | |||
} catch (Exception e) { | |||
throw new InternalAuthenticationServiceException("授权失败:", e); | |||
} | |||
} | |||
protected void setDetails(HttpServletRequest request, UsernamePasswordAuthToken authRequest) { | |||
authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); | |||
} | |||
} |
@@ -1,65 +0,0 @@ | |||
package com.ningdatech.pmapi.user.security.auth.password; | |||
import org.springframework.security.authentication.AuthenticationProvider; | |||
import org.springframework.security.authentication.BadCredentialsException; | |||
import org.springframework.security.authentication.InternalAuthenticationServiceException; | |||
import org.springframework.security.core.Authentication; | |||
import org.springframework.security.core.AuthenticationException; | |||
import org.springframework.security.core.userdetails.UserDetails; | |||
import org.springframework.security.core.userdetails.UserDetailsService; | |||
import org.springframework.security.crypto.password.PasswordEncoder; | |||
/** | |||
* @Author LiuXinXin | |||
* @Date 2020/8/3 8:55 下午 | |||
* @Version 1.0 | |||
**/ | |||
public class UsernamePasswordAuthProvider implements AuthenticationProvider { | |||
private UserDetailsService userDetailsService; | |||
private PasswordEncoder passwordEncoder; | |||
@Override | |||
public Authentication authenticate(Authentication authentication) throws AuthenticationException { | |||
if (!(authentication instanceof UsernamePasswordAuthToken)) { | |||
throw new RuntimeException("CustomAuthProvider 只支持 CustomAuthToken"); | |||
} | |||
UsernamePasswordAuthToken authenticationToken = (UsernamePasswordAuthToken) authentication; | |||
UserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal()); | |||
if (user == null) { | |||
throw new InternalAuthenticationServiceException("can not get user info!"); | |||
} | |||
// TODO 开发使用暂时关闭账号密码验证 | |||
// additionalAuthenticationChecks(user, authenticationToken); | |||
// 校验用户是否有当前端的登陆权限 | |||
// 将用户定义的user放入token中,这样可以在session中查询到所有自定义的用户信息 | |||
return new UsernamePasswordAuthToken(user, user.getPassword(), user.getAuthorities()); | |||
} | |||
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthToken authentication) | |||
throws AuthenticationException { | |||
if (authentication.getCredentials() == null) { | |||
throw new BadCredentialsException("login fail! password is null"); | |||
} | |||
String presentedPassword = authentication.getCredentials().toString(); | |||
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { | |||
throw new BadCredentialsException("login fail! password is error"); | |||
} | |||
} | |||
@Override | |||
public boolean supports(Class<?> authentication) { | |||
return UsernamePasswordAuthToken.class.isAssignableFrom(authentication); | |||
} | |||
public void setUserDetailsService(UserDetailsService userDetailsService) { | |||
this.userDetailsService = userDetailsService; | |||
} | |||
public void setPasswordEncoder(PasswordEncoder passwordEncoder) { | |||
this.passwordEncoder = passwordEncoder; | |||
} | |||
} |
@@ -1,20 +1,12 @@ | |||
#专有钉钉 | |||
integration: | |||
zzd: | |||
zwdd: | |||
#扫码 | |||
app-auth-key: expert-base_dingoa-c5nnefYVnie | |||
app-auth-secret: nm8qtST8uK431HYrjr7srcE23sT4889QgMcYFM3L | |||
# #免登/获取信息 | |||
# app-key: file-manage-4Mjx9358wuxjyYFjY3 | |||
# app-secret: hE41938wqyQ5LOpc1QDRA9e7gb5YugoClWD3nY4O | |||
app-auth-key: ls-rebuild_dingoa-rgeWs3YVr26z | |||
app-auth-secret: 37qCe6ylNMW0N8K2741z0c2b9vJP2gtuMRQQtZ9P | |||
#免登/获取信息 | |||
app-key: ls_rebuild-10c8n5X0707yFV7jURr | |||
app-secret: gN8J3WazyXLMWKDuFmx6C4yaH5lFUY41x8rYLLo6 | |||
#专有钉钉在开发管理工作台,右键查看网页源码realmId: '31141',浙政钉固定196729 | |||
tenantId: 31141 | |||
domain: openplatform.dg-work.cn | |||
# integration.zzd.enabled=true | |||
# #扫码 | |||
# integration.zzd.app-auth-key=file-manage_dingoa-zte2LbiAfIj | |||
# integration.zzd.app-auth-secret=H794aFZf271QbfUr50pbBpBTlXSrWIP71q9RTR34 | |||
# integration.zzd.domain=openplatform.dg-work.cn | |||
domain: openplatform.dg-work.cn |
@@ -1,11 +1,12 @@ | |||
#专有钉钉 | |||
ding: | |||
#扫码 | |||
app-auth-key: expert-base_dingoa-c5nnefYVnie | |||
app-auth-secret: nm8qtST8uK431HYrjr7srcE23sT4889QgMcYFM3L | |||
# #免登/获取信息 | |||
app-key: file-manage-4Mjx9358wuxjyYFjY3 | |||
app-secret: hE41938wqyQ5LOpc1QDRA9e7gb5YugoClWD3nY4O | |||
#专有钉钉在开发管理工作台,右键查看网页源码realmId: '31141',浙政钉固定196729 | |||
tenantId: 31141 | |||
domain: openplatform.dg-work.cn | |||
integration: | |||
zwdd: | |||
#扫码 | |||
app-auth-key: ls-rebuild_dingoa-rgeWs3YVr26z | |||
app-auth-secret: 37qCe6ylNMW0N8K2741z0c2b9vJP2gtuMRQQtZ9P | |||
#免登/获取信息 | |||
app-key: ls_rebuild-10c8n5X0707yFV7jURr | |||
app-secret: gN8J3WazyXLMWKDuFmx6C4yaH5lFUY41x8rYLLo6 | |||
#专有钉钉在开发管理工作台,右键查看网页源码realmId: '31141',浙政钉固定196729 | |||
tenantId: 31141 | |||
domain: openplatform.dg-work.cn |
@@ -2,7 +2,7 @@ security: | |||
auth: | |||
auth-require-url: /api/v1/user/auth/auth-require | |||
invalid-session-url: /api/v1/user/auth/invalid-session | |||
password-login-url: /api/v1/user/auth/login/password | |||
password-login-url: /api/v1/user/auth/login | |||
logout-url: /api/v1/user/auth/logout | |||
ignore-auth-urls: | |||
- /v2/api-docs | |||
@@ -2,7 +2,7 @@ security: | |||
auth: | |||
auth-require-url: /api/v1/user/auth/auth-require | |||
invalid-session-url: /api/v1/user/auth/invalid-session | |||
password-login-url: /api/v1/user/auth/login/password | |||
password-login-url: /api/v1/user/auth/login | |||
logout-url: /api/v1/user/auth/logout | |||
ignore-auth-urls: | |||
- /v2/api-docs | |||