@@ -0,0 +1,35 @@ | |||||
package com.hz.pm.api.common.config; | |||||
import cn.hutool.core.util.RandomUtil; | |||||
import lombok.Data; | |||||
import org.springframework.boot.context.properties.ConfigurationProperties; | |||||
/** | |||||
* <p> | |||||
* AuthCodeProperties | |||||
* </p> | |||||
* | |||||
* @author WendyYang | |||||
* @since 00:15 2023/12/21 | |||||
*/ | |||||
@Data | |||||
@ConfigurationProperties(prefix = "auth-code") | |||||
public class AuthCodeProperties { | |||||
private String secretKey; | |||||
/** | |||||
* authCode失效时间(单位:秒) | |||||
*/ | |||||
private Integer expireTime = 30; | |||||
/** | |||||
* authCode长度(最大:16~32) | |||||
*/ | |||||
private Integer length = 16; | |||||
public static void main(String[] args) { | |||||
System.out.println("secretKey:" + RandomUtil.randomString(32)); | |||||
} | |||||
} |
@@ -20,8 +20,15 @@ public class WebProperties { | |||||
public static String webUrl; | public static String webUrl; | ||||
public static String apiHost; | |||||
public static String provincialUrl; | public static String provincialUrl; | ||||
@Value("${api-host:}") | |||||
private void setApiHost(String host) { | |||||
apiHost = host; | |||||
} | |||||
@Value("${expert-registration.url:/expertEnroll}") | @Value("${expert-registration.url:/expertEnroll}") | ||||
private void setExpertRegistrationUrl(String url) { | private void setExpertRegistrationUrl(String url) { | ||||
expertRegistrationUrl = url; | expertRegistrationUrl = url; | ||||
@@ -2,9 +2,17 @@ package com.hz.pm.api.user.controller; | |||||
import com.fasterxml.jackson.databind.ObjectMapper; | import com.fasterxml.jackson.databind.ObjectMapper; | ||||
import com.hz.pm.api.common.config.AuthCodeProperties; | |||||
import com.hz.pm.api.meeting.entity.config.WebProperties; | |||||
import com.hz.pm.api.user.manage.AgentLoginManage; | |||||
import com.hz.pm.api.user.manage.AuthCodeManage; | |||||
import com.hz.pm.api.user.model.vo.AuthCodeVO; | |||||
import com.hz.pm.api.user.util.LoginUserUtil; | |||||
import com.ningdatech.basic.exception.BizException; | |||||
import com.ningdatech.basic.util.StrPool; | import com.ningdatech.basic.util.StrPool; | ||||
import com.hz.pm.api.common.model.constant.BizConst; | import com.hz.pm.api.common.model.constant.BizConst; | ||||
import com.hz.pm.api.user.security.auth.constants.SessionTimeConstant; | import com.hz.pm.api.user.security.auth.constants.SessionTimeConstant; | ||||
import com.ningdatech.log.annotation.WebLog; | |||||
import io.swagger.annotations.Api; | import io.swagger.annotations.Api; | ||||
import io.swagger.annotations.ApiImplicitParam; | import io.swagger.annotations.ApiImplicitParam; | ||||
import io.swagger.annotations.ApiImplicitParams; | import io.swagger.annotations.ApiImplicitParams; | ||||
@@ -35,6 +43,10 @@ import java.io.IOException; | |||||
public class UserAuthController { | public class UserAuthController { | ||||
private final ObjectMapper objectMapper; | private final ObjectMapper objectMapper; | ||||
private final AuthCodeManage authCodeManage; | |||||
private final AgentLoginManage agentLoginManage; | |||||
private static final String AGENT_LOGIN_PATH = "/api/v1/user/auth/agent-login"; | |||||
@PostMapping(value = "/login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) | @PostMapping(value = "/login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) | ||||
@ApiOperation(value = "登陆") | @ApiOperation(value = "登陆") | ||||
@@ -81,12 +93,43 @@ public class UserAuthController { | |||||
response.getWriter().write(objectMapper.writeValueAsString(BizConst.UNAUTHENTICATED)); | response.getWriter().write(objectMapper.writeValueAsString(BizConst.UNAUTHENTICATED)); | ||||
} | } | ||||
@PostMapping(value = "/agent-login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) | |||||
@PostMapping(value = "/proxy/agent-login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) | |||||
@ApiOperation(value = "代登陆") | @ApiOperation(value = "代登陆") | ||||
@ApiImplicitParams({ | |||||
@ApiImplicitParam(name = "userId", value = "账号", required = true, paramType = "form", dataType = "String")}) | |||||
public void agentLogin(@RequestParam(value = "userId") String userId) { | |||||
// 不实现任何内容,只是为了出api文档 | |||||
@WebLog("代登录(代理接口)") | |||||
public void agentLoginProxy(@RequestParam(value = "userId") Long userId, | |||||
@RequestParam(value = "username", required = false, defaultValue = "") String username, | |||||
@RequestParam(value = "timestamp") long timestamp, | |||||
@RequestParam(value = "sign") String sign, | |||||
HttpServletRequest request, | |||||
HttpServletResponse response) throws IOException { | |||||
if (System.currentTimeMillis() - timestamp > 5000) { | |||||
throw BizException.wrap("签名已过期"); | |||||
} | |||||
if (LoginUserUtil.getUserId().equals(userId)) { | |||||
throw BizException.wrap("代登录用户无效"); | |||||
} | |||||
String targetUserId = String.valueOf(userId); | |||||
if (!agentLoginManage.agentLoginProxySignCheck(targetUserId, sign)) { | |||||
throw BizException.wrap("签名错误"); | |||||
} | |||||
String authCode = authCodeManage.generateAuthCode(targetUserId); | |||||
String urlParam = "?userId=" + userId + "&username=" + username + "&authCode=" + authCode; | |||||
String path = WebProperties.apiHost + request.getContextPath() + AGENT_LOGIN_PATH; | |||||
response.sendRedirect(path + urlParam); | |||||
} | |||||
@PostMapping(value = "/getAuthCode", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) | |||||
public AuthCodeVO getAuthCode(@RequestParam(value = "userId") String userId, | |||||
@RequestParam(value = "sign") String sign) { | |||||
String authCode = authCodeManage.generateAuthCode(userId, sign); | |||||
return new AuthCodeVO(authCode); | |||||
} | |||||
@GetMapping(value = "/agent-login") | |||||
@ApiOperation(value = "代登陆") | |||||
public void agentLogin(@RequestParam(value = "userId") String userId, | |||||
@RequestParam(value = "username") String username, | |||||
@RequestParam(value = "authCode") String authCode) { | |||||
} | } | ||||
@PostMapping(value = "/mh-login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) | @PostMapping(value = "/mh-login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) | ||||
@@ -0,0 +1,32 @@ | |||||
package com.hz.pm.api.user.manage; | |||||
import cn.hutool.crypto.SecureUtil; | |||||
import cn.hutool.crypto.digest.HMac; | |||||
import com.hz.pm.api.user.util.LoginUserUtil; | |||||
import org.springframework.beans.factory.annotation.Value; | |||||
import org.springframework.stereotype.Component; | |||||
/** | |||||
* <p> | |||||
* AgentLoginProxyManage | |||||
* </p> | |||||
* | |||||
* @author WendyYang | |||||
* @since 09:35 2023/12/21 | |||||
*/ | |||||
@Component | |||||
public class AgentLoginManage { | |||||
/** | |||||
* 代登录代理接口:secretKey | |||||
*/ | |||||
@Value("${agent-login.proxy.secret-key}") | |||||
private String agentLoginProxySecretKey; | |||||
public boolean agentLoginProxySignCheck(String userId, String sign) { | |||||
HMac hmacMd5 = SecureUtil.hmacMd5(agentLoginProxySecretKey); | |||||
String digestHex = hmacMd5.digestHex(userId + "#" + LoginUserUtil.getUserId()); | |||||
return digestHex.equals(sign); | |||||
} | |||||
} |
@@ -0,0 +1,60 @@ | |||||
package com.hz.pm.api.user.manage; | |||||
import cn.hutool.core.util.RandomUtil; | |||||
import cn.hutool.crypto.SecureUtil; | |||||
import cn.hutool.crypto.digest.HMac; | |||||
import com.hz.pm.api.common.config.AuthCodeProperties; | |||||
import com.ningdatech.basic.exception.BizException; | |||||
import com.ningdatech.cache.model.cache.CacheKey; | |||||
import com.ningdatech.cache.repository.CachePlusOps; | |||||
import lombok.RequiredArgsConstructor; | |||||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | |||||
import org.springframework.stereotype.Component; | |||||
import java.time.Duration; | |||||
/** | |||||
* <p> | |||||
* AuthCodeManage | |||||
* </p> | |||||
* | |||||
* @author WendyYang | |||||
* @since 23:59 2023/12/20 | |||||
*/ | |||||
@Component | |||||
@RequiredArgsConstructor | |||||
@EnableConfigurationProperties(AuthCodeProperties.class) | |||||
public class AuthCodeManage { | |||||
private final CachePlusOps cachePlusOps; | |||||
private final AuthCodeProperties authCodeProperties; | |||||
private String generateAuthCode(String userId, boolean checkSign, String sign) { | |||||
if (checkSign) { | |||||
HMac hmacMd5 = SecureUtil.hmacMd5(authCodeProperties.getSecretKey()); | |||||
String digestHex = hmacMd5.digestHex(userId); | |||||
if (!digestHex.equals(sign)) { | |||||
throw BizException.wrap("获取授权码失败:签名错误"); | |||||
} | |||||
} | |||||
String authCode = RandomUtil.randomString(authCodeProperties.getLength()); | |||||
Duration duration = Duration.ofSeconds(authCodeProperties.getExpireTime()); | |||||
CacheKey key = new CacheKey(userId + "#" + authCode, duration); | |||||
cachePlusOps.set(key, userId); | |||||
return authCode; | |||||
} | |||||
public String generateAuthCode(String userId, String sign) { | |||||
return generateAuthCode(userId, true, sign); | |||||
} | |||||
public String generateAuthCode(String userId) { | |||||
return generateAuthCode(userId, false, null); | |||||
} | |||||
public boolean authCodeCheck(String userId, String authCode) { | |||||
CacheKey key = new CacheKey(userId + "#" + authCode); | |||||
return cachePlusOps.del(key) > 0; | |||||
} | |||||
} |
@@ -0,0 +1,20 @@ | |||||
package com.hz.pm.api.user.model.vo; | |||||
import lombok.AllArgsConstructor; | |||||
import lombok.Data; | |||||
/** | |||||
* <p> | |||||
* AuthCodeVO | |||||
* </p> | |||||
* | |||||
* @author WendyYang | |||||
* @since 00:06 2023/12/21 | |||||
*/ | |||||
@Data | |||||
@AllArgsConstructor | |||||
public class AuthCodeVO { | |||||
private String authCode; | |||||
} |
@@ -1,5 +1,7 @@ | |||||
package com.hz.pm.api.user.security.auth.agent; | package com.hz.pm.api.user.security.auth.agent; | ||||
import com.hz.pm.api.common.util.StrUtils; | |||||
import com.hz.pm.api.user.manage.AuthCodeManage; | |||||
import com.hz.pm.api.user.security.model.WebRequestDetails; | import com.hz.pm.api.user.security.model.WebRequestDetails; | ||||
import com.ningdatech.basic.exception.BizException; | import com.ningdatech.basic.exception.BizException; | ||||
import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
@@ -27,10 +29,15 @@ public class AgentAuthFilter extends AbstractAuthenticationProcessingFilter { | |||||
private static final String USER_ID_PARAMETER = "userId"; | private static final String USER_ID_PARAMETER = "userId"; | ||||
private static final String AUTH_CODE = "authCode"; | |||||
private final AuthCodeManage authCodeManage; | |||||
// =================================================================================================== | // =================================================================================================== | ||||
public AgentAuthFilter(String processingUrl) { | |||||
public AgentAuthFilter(String processingUrl,AuthCodeManage authCodeManage) { | |||||
super(new AntPathRequestMatcher(processingUrl, HttpMethod.POST.name())); | super(new AntPathRequestMatcher(processingUrl, HttpMethod.POST.name())); | ||||
this.authCodeManage = authCodeManage; | |||||
} | } | ||||
// ======================================================================================================== | // ======================================================================================================== | ||||
@@ -41,12 +48,17 @@ public class AgentAuthFilter extends AbstractAuthenticationProcessingFilter { | |||||
if (request.getMethod().equals(HttpMethod.POST.name())) { | if (request.getMethod().equals(HttpMethod.POST.name())) { | ||||
throw new AuthenticationServiceException("请求方法错误"); | throw new AuthenticationServiceException("请求方法错误"); | ||||
} | } | ||||
String userId = request.getParameter(USER_ID_PARAMETER); | |||||
String userId = StrUtils.trim(request.getParameter(USER_ID_PARAMETER)); | |||||
if (StringUtils.isBlank(userId)) { | if (StringUtils.isBlank(userId)) { | ||||
throw new BadCredentialsException("用户id 不能为空"); | |||||
throw new BadCredentialsException("用户ID不能为空"); | |||||
} | |||||
String authCode = StrUtils.trim(request.getParameter(AUTH_CODE)); | |||||
if (StringUtils.isBlank(userId)) { | |||||
throw new BadCredentialsException("授权码不能为空"); | |||||
} | |||||
if (!authCodeManage.authCodeCheck(userId, authCode)) { | |||||
throw new BadCredentialsException("授权码已过期"); | |||||
} | } | ||||
userId = trim(userId); | |||||
try { | try { | ||||
AgentAuthToken authRequest = new AgentAuthToken(userId, userId); | AgentAuthToken authRequest = new AgentAuthToken(userId, userId); | ||||
authRequest.setDetails(new WebRequestDetails(request)); | authRequest.setDetails(new WebRequestDetails(request)); | ||||
@@ -1,5 +1,6 @@ | |||||
package com.hz.pm.api.user.security.auth.agent; | package com.hz.pm.api.user.security.auth.agent; | ||||
import com.hz.pm.api.user.manage.AuthCodeManage; | |||||
import com.hz.pm.api.user.security.config.AuthProperties; | import com.hz.pm.api.user.security.config.AuthProperties; | ||||
import org.springframework.beans.factory.annotation.Qualifier; | import org.springframework.beans.factory.annotation.Qualifier; | ||||
import org.springframework.security.authentication.AuthenticationManager; | import org.springframework.security.authentication.AuthenticationManager; | ||||
@@ -28,21 +29,23 @@ public class AgentAuthSecurityConfig extends SecurityConfigurerAdapter<DefaultSe | |||||
protected final AuthenticationFailureHandler defaultLoginFailureHandler; | protected final AuthenticationFailureHandler defaultLoginFailureHandler; | ||||
private final UserDetailsService agentLoginUserDetailService; | private final UserDetailsService agentLoginUserDetailService; | ||||
private final AuthProperties authProperties; | private final AuthProperties authProperties; | ||||
private final AuthCodeManage authCodeManage; | |||||
public AgentAuthSecurityConfig(@Qualifier(value = "defaultLoginSuccessHandler") AuthenticationSuccessHandler loginSuccessHandler, | public AgentAuthSecurityConfig(@Qualifier(value = "defaultLoginSuccessHandler") AuthenticationSuccessHandler loginSuccessHandler, | ||||
@Qualifier(value = "defaultLoginFailureHandler") AuthenticationFailureHandler loginFailureHandler, | @Qualifier(value = "defaultLoginFailureHandler") AuthenticationFailureHandler loginFailureHandler, | ||||
@Qualifier(value = "agentLoginUserDetailService") UserDetailsService agentLoginUserDetailService, | @Qualifier(value = "agentLoginUserDetailService") UserDetailsService agentLoginUserDetailService, | ||||
AuthProperties authProperties) { | |||||
AuthProperties authProperties, | |||||
AuthCodeManage authCodeManage) { | |||||
this.defaultLoginSuccessHandler = loginSuccessHandler; | this.defaultLoginSuccessHandler = loginSuccessHandler; | ||||
this.defaultLoginFailureHandler = loginFailureHandler; | this.defaultLoginFailureHandler = loginFailureHandler; | ||||
this.agentLoginUserDetailService = agentLoginUserDetailService; | this.agentLoginUserDetailService = agentLoginUserDetailService; | ||||
this.authProperties = authProperties; | this.authProperties = authProperties; | ||||
this.authCodeManage = authCodeManage; | |||||
} | } | ||||
@Override | @Override | ||||
public void configure(HttpSecurity http) { | public void configure(HttpSecurity http) { | ||||
AgentAuthFilter agentAuthFilter = | |||||
new AgentAuthFilter(authProperties.getAgentLoginUrl()); | |||||
AgentAuthFilter agentAuthFilter = new AgentAuthFilter(authProperties.getAgentLoginUrl(), authCodeManage); | |||||
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class); | AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class); | ||||
agentAuthFilter.setAuthenticationManager(authenticationManager); | agentAuthFilter.setAuthenticationManager(authenticationManager); | ||||
agentAuthFilter.setAuthenticationSuccessHandler(defaultLoginSuccessHandler); | agentAuthFilter.setAuthenticationSuccessHandler(defaultLoginSuccessHandler); | ||||
@@ -254,4 +254,9 @@ mh: | |||||
expert-qr-code-url: https://jiema.wwei.cn/uploads/2023/12/28/658d7a3f15f06.jpg | expert-qr-code-url: https://jiema.wwei.cn/uploads/2023/12/28/658d7a3f15f06.jpg | ||||
file: | file: | ||||
down-url: https://weixin.hzszxc.hzswb.cn:8443/mh-gateway/oss/oss/previewFileLogin | down-url: https://weixin.hzszxc.hzswb.cn:8443/mh-gateway/oss/oss/previewFileLogin | ||||
detail-url: https://weixin.hzszxc.hzswb.cn:8443/mh-gateway/oss/ossfile/getFileInfoList | |||||
detail-url: https://weixin.hzszxc.hzswb.cn:8443/mh-gateway/oss/ossfile/getFileInfoList | |||||
auth-code: | |||||
secret-key: nqkmzqojg5j4eiypr3rb8s7nb4noa8b2 | |||||
agent-login: | |||||
proxy: | |||||
secret-key: nqkwiqojg7g4eiypr3rb8s7nb4noa8b2 |
@@ -258,4 +258,10 @@ sync-mh-user: | |||||
sync-mh-expert: | sync-mh-expert: | ||||
open: false | open: false | ||||
sync-mh-unit: | sync-mh-unit: | ||||
open: false | |||||
open: false | |||||
auth-code: | |||||
secret-key: uqrvd2bani4fercnisua1cqxjwk1neym | |||||
agent-login: | |||||
proxy: | |||||
secret-key: tqkwiqojg5j4eiypr3rb8w7nb4noa8b2 |
@@ -19,6 +19,7 @@ security: | |||||
- /api/v1/user/auth/login/password | - /api/v1/user/auth/login/password | ||||
- /api/v1/user/auth/forget-password | - /api/v1/user/auth/forget-password | ||||
- /api/v1/user/auth/common-login | - /api/v1/user/auth/common-login | ||||
- /api/v1/user/auth/common-login | |||||
- /doc.html | - /doc.html | ||||
- /ok.html | - /ok.html | ||||
- /open/api/** | - /open/api/** | ||||