@@ -1,6 +1,5 @@ | |||
package com.ningdatech.pmapi.common.handler; | |||
import cn.hutool.json.JSONUtil; | |||
import com.ningdatech.basic.model.ApiResponse; | |||
import org.springframework.core.MethodParameter; | |||
import org.springframework.http.MediaType; | |||
@@ -23,6 +23,7 @@ import org.springframework.util.Assert; | |||
import java.time.Duration; | |||
import java.time.LocalDateTime; | |||
import java.util.Collections; | |||
import java.util.Objects; | |||
/** | |||
* @author liuxinxin | |||
@@ -81,7 +82,7 @@ public class SmsManage { | |||
cachePlusOps.set(new CacheKey(cacheKey, Duration.ofMinutes(verificationCodeTypeEnum.getExpireTime())), cache); | |||
String limitKey = SmsRedisKeyUtils.smsSendLimitKey(verificationCodeTypeEnum, request.getMobile()); | |||
if (StringUtils.isNotBlank(cachePlusOps.get(limitKey))) { | |||
if (Objects.nonNull(cachePlusOps.get(limitKey))) { | |||
long limitCount = cachePlusOps.incr(new CacheKey(limitKey, Duration.ofSeconds(DateUtil.restSecondsFromNowToNoon()))); | |||
// 超出单日发送次数之后直接锁定 | |||
if (limitCount >= verificationCodeTypeEnum.getSendTimesByDay().longValue()) { | |||
@@ -81,4 +81,11 @@ public class UserAuthController { | |||
response.getWriter().write(objectMapper.writeValueAsString(BizConst.UNAUTHENTICATED)); | |||
} | |||
@PostMapping(value = "/agent-login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) | |||
@ApiOperation(value = "代登陆") | |||
@ApiImplicitParams({ | |||
@ApiImplicitParam(name = "userId", value = "账号", required = true, paramType = "form", dataType = "String")}) | |||
public void agentLogin(@RequestParam(value = "userId", required = true) String userId) { | |||
// 不实现任何内容,只是为了出api文档 | |||
} | |||
} |
@@ -59,10 +59,4 @@ public class UserInfoController { | |||
return userInfoManage.currentUserInfo(); | |||
} | |||
@ApiOperation(value = "代登陆", notes = "代登陆") | |||
@PostMapping("/generation-login") | |||
public void generationLogin(@Valid @RequestBody ReqGenerationLoginPO reqGenerationLoginPO, HttpServletRequest httpServletRequest) { | |||
userInfoManage.generationLogin(reqGenerationLoginPO); | |||
} | |||
} |
@@ -17,7 +17,10 @@ import com.ningdatech.pmapi.sys.service.IRoleService; | |||
import com.ningdatech.pmapi.sys.service.IUserRoleService; | |||
import com.ningdatech.pmapi.user.constant.UserAvailableEnum; | |||
import com.ningdatech.pmapi.user.entity.UserInfo; | |||
import com.ningdatech.pmapi.user.model.po.*; | |||
import com.ningdatech.pmapi.user.model.po.ReqUserDetailEditPO; | |||
import com.ningdatech.pmapi.user.model.po.ReqUserDetailPO; | |||
import com.ningdatech.pmapi.user.model.po.ReqUserDisableOrEnablePO; | |||
import com.ningdatech.pmapi.user.model.po.ReqUserInfoListPO; | |||
import com.ningdatech.pmapi.user.model.vo.ResUserDetailVO; | |||
import com.ningdatech.pmapi.user.model.vo.ResUserInfoListVO; | |||
import com.ningdatech.pmapi.user.model.vo.UserRoleVO; | |||
@@ -25,7 +28,6 @@ import com.ningdatech.pmapi.user.security.auth.model.UserFullInfoDTO; | |||
import com.ningdatech.pmapi.user.service.IUserInfoService; | |||
import com.ningdatech.pmapi.user.util.LoginUserUtil; | |||
import lombok.RequiredArgsConstructor; | |||
import org.springframework.security.core.userdetails.UserDetailsService; | |||
import org.springframework.stereotype.Component; | |||
import org.springframework.transaction.annotation.Transactional; | |||
@@ -250,7 +252,7 @@ public class UserInfoManage { | |||
} | |||
resUserDetailVO.setUserRoleInfoList(userRoleInfoList); | |||
UserFullInfoDTO userFullInfo = userInfoHelper.getUserFullInfo(userId); | |||
if(Objects.nonNull(userFullInfo)){ | |||
if (Objects.nonNull(userFullInfo)) { | |||
resUserDetailVO.setOrgCode(userFullInfo.getOrganizationCode()); | |||
resUserDetailVO.setOrgName(userFullInfo.getOrganizationName()); | |||
resUserDetailVO.setRegionCode(userFullInfo.getRegionCode()); | |||
@@ -258,39 +260,4 @@ public class UserInfoManage { | |||
return resUserDetailVO; | |||
} | |||
public void generationLogin(ReqGenerationLoginPO reqGenerationLoginPO) { | |||
Long userId = reqGenerationLoginPO.getUserId(); | |||
UserInfo userInfo = iUserInfoService.getById(userId); | |||
if (Objects.isNull(userInfo)) { | |||
throw new BizException("该员工账号处于禁用状态中,无法使用"); | |||
} | |||
if (!UserAvailableEnum.ENABLE.name().equals(userInfo.getAvailable())) { | |||
throw new BizException("该员工账号处于禁用状态中,无法使用"); | |||
} | |||
UserFullInfoDTO userFullInfo = userInfoHelper.getUserFullInfo(userId); | |||
// ReqGenerationLoginPO reqGenerationLoginPO | |||
} | |||
// public void autoLogin(Long userId){ | |||
// userDetailsService.loadUserByUsername(userId + UserDeatilsServiceConstant.USER_DETAILS_SERVICE_SEPARATOR + LoginTypeEnum.USERNAME_PASSWORD_LOGIN.name()); | |||
// | |||
// CredentialAuthToken token = new CredentialAuthToken(email, password); | |||
// try { | |||
// token.setDetails(new WebAuthenticationDetails(httpServletRequest)); | |||
// UsernamePasswordAuthToken authenticatedUser = (UsernamePasswordAuthToken)usernamePasswordAuthSecurityConfig | |||
// .getAuthenticationManager().authenticate(token); | |||
// SecurityContextHolder.getContext().setAuthentication(authenticatedUser); | |||
// httpServletRequest.getSession().setAttribute( | |||
// HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext()); | |||
// String sessionId = httpServletRequest.getSession().getId(); | |||
// putSessionIdToCache(LoginUserUtil.getUserId(), sessionId); | |||
// } catch ( | |||
// AuthenticationException e) { | |||
// throw new RuntimeException("autoLogIn Authentication failed!", e); | |||
// } | |||
// } | |||
} |
@@ -29,6 +29,11 @@ public class AuthProperties { | |||
private String passwordLoginUrl; | |||
/** | |||
* 代登陆接口 | |||
*/ | |||
private String agentLoginUrl; | |||
private String logoutUrl; | |||
private List<String> ignoreAuthUrls; | |||
@@ -0,0 +1,74 @@ | |||
package com.ningdatech.pmapi.user.security.auth.agent; | |||
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.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 AgentAuthFilter extends AbstractAuthenticationProcessingFilter { | |||
private boolean postOnly = true; | |||
private static final String USER_ID_PARAMETER = "userId"; | |||
// ~ Constructors | |||
// =================================================================================================== | |||
public AgentAuthFilter(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 userId = request.getParameter(USER_ID_PARAMETER); | |||
if (StringUtils.isBlank(userId)) { | |||
throw new BadCredentialsException("用户id 不能为空"); | |||
} | |||
userId = trim(userId); | |||
try { | |||
AgentAuthToken authRequest = new AgentAuthToken(userId, userId); | |||
// 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, AgentAuthToken authRequest) { | |||
authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); | |||
} | |||
private String trim(String trimStr) { | |||
if (StringUtils.isNotBlank(trimStr)) { | |||
return trimStr.trim(); | |||
} | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
package com.ningdatech.pmapi.user.security.auth.agent; | |||
import org.springframework.security.authentication.AuthenticationProvider; | |||
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; | |||
/** | |||
* @Author LiuXinXin | |||
* @Date 2020/8/3 8:55 下午 | |||
* @Version 1.0 | |||
**/ | |||
public class AgentAuthProvider implements AuthenticationProvider { | |||
private UserDetailsService userDetailsService; | |||
@Override | |||
public Authentication authenticate(Authentication authentication) throws AuthenticationException { | |||
if (!(authentication instanceof AgentAuthToken)) { | |||
throw new RuntimeException("CustomAuthProvider 只支持 CustomAuthToken"); | |||
} | |||
AgentAuthToken authenticationToken = (AgentAuthToken) authentication; | |||
String principal = (String) authenticationToken.getPrincipal(); | |||
UserDetails user = userDetailsService.loadUserByUsername(principal); | |||
// 将用户定义的user放入token中,这样可以在session中查询到所有自定义的用户信息 | |||
return new AgentAuthToken(user, user.getPassword(), user.getAuthorities()); | |||
} | |||
@Override | |||
public boolean supports(Class<?> authentication) { | |||
return AgentAuthToken.class.isAssignableFrom(authentication); | |||
} | |||
public void setUserDetailsService(UserDetailsService userDetailsService) { | |||
this.userDetailsService = userDetailsService; | |||
} | |||
} |
@@ -0,0 +1,55 @@ | |||
package com.ningdatech.pmapi.user.security.auth.agent; | |||
import com.ningdatech.pmapi.user.security.auth.AuthProperties; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.beans.factory.annotation.Qualifier; | |||
import org.springframework.security.authentication.AuthenticationManager; | |||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter; | |||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | |||
import org.springframework.security.core.userdetails.UserDetailsService; | |||
import org.springframework.security.web.DefaultSecurityFilterChain; | |||
import org.springframework.security.web.authentication.AuthenticationFailureHandler; | |||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler; | |||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | |||
import org.springframework.stereotype.Component; | |||
/** | |||
* 账号密码登陆的认证配置 | |||
*/ | |||
@Component | |||
public class AgentAuthSecurityConfig | |||
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> { | |||
@Autowired | |||
@Qualifier(value = "defaultLoginSuccessHandler") | |||
protected AuthenticationSuccessHandler defaultLoginSuccessHandler; | |||
@Autowired | |||
@Qualifier(value = "defaultLoginFailureHandler") | |||
protected AuthenticationFailureHandler defaultLoginFailureHandler; | |||
@Autowired | |||
@Qualifier(value = "agentLoginUserDetailService") | |||
private UserDetailsService agentLoginUserDetailService; | |||
@Autowired | |||
private AuthProperties authProperties; | |||
private AuthenticationManager authenticationManager; | |||
@Override | |||
public void configure(HttpSecurity http) throws Exception { | |||
AgentAuthFilter agentAuthFilter = | |||
new AgentAuthFilter(authProperties.getAgentLoginUrl()); | |||
authenticationManager = http.getSharedObject(AuthenticationManager.class); | |||
agentAuthFilter.setAuthenticationManager(authenticationManager); | |||
agentAuthFilter.setAuthenticationSuccessHandler(defaultLoginSuccessHandler); | |||
agentAuthFilter.setAuthenticationFailureHandler(defaultLoginFailureHandler); | |||
AgentAuthProvider authenticationProvider = new AgentAuthProvider(); | |||
authenticationProvider.setUserDetailsService(agentLoginUserDetailService); | |||
http.authenticationProvider(authenticationProvider).addFilterAfter(agentAuthFilter, | |||
UsernamePasswordAuthenticationFilter.class); | |||
} | |||
} |
@@ -0,0 +1,76 @@ | |||
package com.ningdatech.pmapi.user.security.auth.agent; | |||
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; | |||
import java.util.Collection; | |||
/** | |||
* @Author LiuXinXin | |||
* @Date 2020/8/3 8:52 下午 | |||
* @Version 1.0 | |||
**/ | |||
public class AgentAuthToken extends AbstractAuthenticationToken { | |||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; | |||
private final Object principal; | |||
private final Object credentials; | |||
/** | |||
* 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 AgentAuthToken(String principal, String credentials) { | |||
super(null); | |||
this.principal = principal; | |||
this.credentials = credentials; | |||
setAuthenticated(false); | |||
} | |||
/** | |||
* This constructor should only be used by <code>AuthenticationManager</code> or <code>AuthenticationProvider</code> | |||
* implementations that are satisfied with producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>) | |||
* authentication token. | |||
* | |||
* @param principal | |||
* @param authorities | |||
*/ | |||
public AgentAuthToken(Object principal, Object credentials, | |||
Collection<? extends GrantedAuthority> authorities) { | |||
super(authorities); | |||
this.principal = principal; | |||
this.credentials = credentials; | |||
// must use super, as we override | |||
super.setAuthenticated(true); | |||
} | |||
@Override | |||
public Object getCredentials() { | |||
return this.credentials; | |||
} | |||
@Override | |||
public Object getPrincipal() { | |||
return this.principal; | |||
} | |||
@Override | |||
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { | |||
if (isAuthenticated) { | |||
throw new IllegalArgumentException( | |||
"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead"); | |||
} | |||
super.setAuthenticated(false); | |||
} | |||
@Override | |||
public void eraseCredentials() { | |||
super.eraseCredentials(); | |||
} | |||
} |
@@ -0,0 +1,48 @@ | |||
package com.ningdatech.pmapi.user.security.auth.agent; | |||
import com.ningdatech.pmapi.user.manage.UserAuthLoginManage; | |||
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("agentLoginUserDetailService") | |||
@RequiredArgsConstructor | |||
public class AgentLoginUserDetailService implements UserDetailsService { | |||
private final UserAuthLoginManage userAuthLoginManage; | |||
@Override | |||
public UserInfoDetails loadUserByUsername(String username) throws UsernameNotFoundException { | |||
final Long userId = Long.parseLong(username); | |||
UserFullInfoDTO userFullInfoDTO = userAuthLoginManage.getUserFullInfo(userId); | |||
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.setUserRoleList(userFullInfoDTO.getUserRoleList()); | |||
userInfoDetails.setRegionCode(userFullInfoDTO.getRegionCode()); | |||
userInfoDetails.setIdentifier(userFullInfoDTO.getIdentifier()); | |||
userInfoDetails.setPassword(userFullInfoDTO.getCredential()); | |||
userInfoDetails.setOrganizationCode(userFullInfoDTO.getOrganizationCode()); | |||
userInfoDetails.setOrganizationName(userFullInfoDTO.getOrganizationName()); | |||
return userInfoDetails; | |||
} | |||
} |
@@ -3,6 +3,7 @@ security: | |||
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 | |||
agent-login-url: /api/v1/user/auth/agent-login | |||
logout-url: /api/v1/user/auth/logout | |||
ignore-auth-urls: | |||
- /v2/api-docs | |||