diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/common/handler/GlobalResponseHandler.java b/pmapi/src/main/java/com/ningdatech/pmapi/common/handler/GlobalResponseHandler.java index 18cbc5c..e1b5946 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/common/handler/GlobalResponseHandler.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/common/handler/GlobalResponseHandler.java @@ -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; diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/sms/manage/SmsManage.java b/pmapi/src/main/java/com/ningdatech/pmapi/sms/manage/SmsManage.java index 5c6fb2e..38a2274 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/sms/manage/SmsManage.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/sms/manage/SmsManage.java @@ -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()) { diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/controller/UserAuthController.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/controller/UserAuthController.java index d9fc07b..1ba09a6 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/user/controller/UserAuthController.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/controller/UserAuthController.java @@ -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文档 + } } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/controller/UserInfoController.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/controller/UserInfoController.java index 9c4e3c8..7bc36b1 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/user/controller/UserInfoController.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/controller/UserInfoController.java @@ -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); - } - } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/manage/UserInfoManage.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/manage/UserInfoManage.java index 2b21d96..52d9f41 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/user/manage/UserInfoManage.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/manage/UserInfoManage.java @@ -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); -// } -// } - - } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/AuthProperties.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/AuthProperties.java index 16d9091..938f339 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/AuthProperties.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/AuthProperties.java @@ -29,6 +29,11 @@ public class AuthProperties { private String passwordLoginUrl; + /** + * 代登陆接口 + */ + private String agentLoginUrl; + private String logoutUrl; private List ignoreAuthUrls; diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/agent/AgentAuthFilter.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/agent/AgentAuthFilter.java new file mode 100644 index 0000000..e6a9128 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/agent/AgentAuthFilter.java @@ -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; + } +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/agent/AgentAuthProvider.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/agent/AgentAuthProvider.java new file mode 100644 index 0000000..f1f3376 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/agent/AgentAuthProvider.java @@ -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; + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/agent/AgentAuthSecurityConfig.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/agent/AgentAuthSecurityConfig.java new file mode 100644 index 0000000..802a349 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/agent/AgentAuthSecurityConfig.java @@ -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 { + + @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); + } +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/agent/AgentAuthToken.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/agent/AgentAuthToken.java new file mode 100644 index 0000000..3f0c3c2 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/agent/AgentAuthToken.java @@ -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 + * UsernamePasswordAuthenticationToken, as the {@link #isAuthenticated()} will return + * false. + */ + public AgentAuthToken(String principal, String credentials) { + super(null); + this.principal = principal; + this.credentials = credentials; + setAuthenticated(false); + } + + /** + * This constructor should only be used by AuthenticationManager or AuthenticationProvider + * implementations that are satisfied with producing a trusted (i.e. {@link #isAuthenticated()} = true) + * authentication token. + * + * @param principal + * @param authorities + */ + public AgentAuthToken(Object principal, Object credentials, + Collection 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(); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/agent/AgentLoginUserDetailService.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/agent/AgentLoginUserDetailService.java new file mode 100644 index 0000000..163441b --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/agent/AgentLoginUserDetailService.java @@ -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; + } +} diff --git a/pmapi/src/main/resources/security/auth-dev.yml b/pmapi/src/main/resources/security/auth-dev.yml index 86f67a6..1bd9564 100644 --- a/pmapi/src/main/resources/security/auth-dev.yml +++ b/pmapi/src/main/resources/security/auth-dev.yml @@ -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