Commit 74d54285 authored by fangxinjiang's avatar fangxinjiang

企业微信登录相关

parent 816d0adc
...@@ -21,10 +21,12 @@ import com.yifu.cloud.plus.v1.yifu.auth.filter.PasswordDecoderFilter; ...@@ -21,10 +21,12 @@ import com.yifu.cloud.plus.v1.yifu.auth.filter.PasswordDecoderFilter;
import com.yifu.cloud.plus.v1.yifu.auth.handler.YifuAuthenticationFailureHandlerImpl; import com.yifu.cloud.plus.v1.yifu.auth.handler.YifuAuthenticationFailureHandlerImpl;
import com.yifu.cloud.plus.v1.yifu.auth.handler.YifuClientLoginSuccessHandler; import com.yifu.cloud.plus.v1.yifu.auth.handler.YifuClientLoginSuccessHandler;
import com.yifu.cloud.plus.v1.yifu.common.security.component.CasAuthenticationProvider; import com.yifu.cloud.plus.v1.yifu.common.security.component.CasAuthenticationProvider;
import com.yifu.cloud.plus.v1.yifu.common.security.component.WxAuthenticationProvider;
import com.yifu.cloud.plus.v1.yifu.common.security.component.YifuDaoAuthenticationProvider; import com.yifu.cloud.plus.v1.yifu.common.security.component.YifuDaoAuthenticationProvider;
import com.yifu.cloud.plus.v1.yifu.common.security.grant.CustomAppAuthenticationProvider; import com.yifu.cloud.plus.v1.yifu.common.security.grant.CustomAppAuthenticationProvider;
import com.yifu.cloud.plus.v1.yifu.common.security.handler.FormAuthenticationFailureHandler; import com.yifu.cloud.plus.v1.yifu.common.security.handler.FormAuthenticationFailureHandler;
import com.yifu.cloud.plus.v1.yifu.common.security.handler.SsoLogoutSuccessHandler; import com.yifu.cloud.plus.v1.yifu.common.security.handler.SsoLogoutSuccessHandler;
import com.yifu.cloud.plus.v1.yifu.common.security.service.WxUserDetailService;
import com.yifu.cloud.plus.v1.yifu.common.security.service.YifuUserDetailsServiceImpl; import com.yifu.cloud.plus.v1.yifu.common.security.service.YifuUserDetailsServiceImpl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows; import lombok.SneakyThrows;
...@@ -67,7 +69,8 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { ...@@ -67,7 +69,8 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Lazy @Lazy
@Autowired @Autowired
private AuthorizationServerTokenServices defaultAuthorizationServerTokenServices; private AuthorizationServerTokenServices defaultAuthorizationServerTokenServices;
@Autowired
private WxUserDetailService wxUserDetailService;
@Autowired @Autowired
private CacheManager cacheManager; private CacheManager cacheManager;
...@@ -85,7 +88,7 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { ...@@ -85,7 +88,7 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
.failureHandler(authenticationFailureHandler()).and().logout() .failureHandler(authenticationFailureHandler()).and().logout()
.logoutSuccessHandler(logoutSuccessHandler()).deleteCookies("JSESSIONID").invalidateHttpSession(true) .logoutSuccessHandler(logoutSuccessHandler()).deleteCookies("JSESSIONID").invalidateHttpSession(true)
.and() .and()
.authorizeRequests().antMatchers("/**/login","/token/**", "/actuator/**", "/mobile/**", "/oauth/token","/weixin/callback").permitAll() .authorizeRequests().antMatchers("/**/login","/token/**", "/actuator/**", "/mobile/**", "/oauth/token","/weixin/callback","/oauth/wxLogin").permitAll()
.anyRequest().authenticated().and().csrf().disable(); .anyRequest().authenticated().and().csrf().disable();
} }
...@@ -103,8 +106,17 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { ...@@ -103,8 +106,17 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
// 自定义的认证模式 // 自定义的认证模式
auth.authenticationProvider(new CustomAppAuthenticationProvider()); auth.authenticationProvider(new CustomAppAuthenticationProvider());
auth.authenticationProvider(casAuthenticationProvider()); auth.authenticationProvider(casAuthenticationProvider());
auth.authenticationProvider(wxAuthenticationProvider());
}
@Bean
public WxAuthenticationProvider wxAuthenticationProvider() {
WxAuthenticationProvider provider = new WxAuthenticationProvider();
// 设置userDetailsService
provider.setUserDetailsService(wxUserDetailService);
// 禁止隐藏用户未找到异常
provider.setHideUserNotFoundExceptions(false);
return provider;
} }
@Bean @Bean
@Override @Override
@SneakyThrows @SneakyThrows
......
package com.yifu.cloud.plus.v1.yifu.auth.config;
import cn.hutool.core.text.StrBuilder;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.CacheConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.CommonConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.SecurityConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.exception.CheckedException;
import com.yifu.cloud.plus.v1.yifu.common.core.util.Common;
import com.yifu.cloud.plus.v1.yifu.common.core.util.EncryptUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @Author: fxj
* @Date: 2025-01-07
* @Description:
* @return: 企业微信配置
**/
@Configuration
@Data
@Slf4j
public class WxConfig {
@Value("${wx.corpid}")
private String corpid;
@Value("${wx.corpsecret}")
private String corpsecret;
@Value("${wx.agentid}")
private String agentid;
@Value("${wx.authUrl}")
private String authUrl;
private static Map<String,String> corpsecretMap = new HashMap<>();
//未授权
private String accossTokenInvliad = "40014";
//请求成功
private String accossTokenSuccess = "0";
//错误吗
private String errcode = "errcode";
@Autowired
private RedisTemplate redisTemplate;
static{
//合同审批(生产)
corpsecretMap.put("1000009","R0nKkvsY-oF41fuQvUXZ-kFG3_g_Ce0bpZt6mByx524");
}
/**
* @param
* @Author: fxj
* @Date: 2025-01-07
* @Description: 获取微信accos_token
* @return: java.lang.String
**/
public String getAccessToken(RestTemplate restTemplate) {
if (Common.isNotNull(agentid)) {
return this.getToken(restTemplate, CacheConstants.WX_ACCOSS_TOKEN.concat(agentid), corpsecret);
}
return this.getToken(restTemplate, CacheConstants.WX_ACCOSS_TOKEN, corpsecret);
}
public String getAccessToken(RestTemplate restTemplate,String agentId)throws AuthenticationServiceException {
if(corpsecretMap.isEmpty()){
throw new AuthenticationServiceException("corpsecretMap为空请联系管理员配置");
}
String thisCorpsecret = corpsecretMap.get(agentId);
if(Common.isEmpty(thisCorpsecret)){
throw new AuthenticationServiceException("未找到对应的corpsecret请联系管理员配置");
}
return this.getToken(restTemplate, CacheConstants.WX_ACCOSS_TOKEN.concat(agentId), thisCorpsecret);
}
/**
* @param restTemplate
* @param tokenKey
* @param corpsecretKey
* @Description: 获取token
* @Author: hgw
* @Date: 2021/3/24 14:43
* @return: java.lang.String
**/
public String getToken(RestTemplate restTemplate,String tokenKey, String corpsecretKey) {
Object wxToken = redisTemplate.opsForValue().get(tokenKey);
if (wxToken != null) {
return String.valueOf(wxToken);
}
String requestTokenUrl = String.format(SecurityConstants.WX_GET_ACCOSS_TOKEN, corpid, corpsecretKey);
String result = restTemplate.getForObject(requestTokenUrl, String.class);
if (Common.isEmpty(result)) {
throw new CheckedException("微信授权失败");
}
String token = JSON.parseObject(result).getString("access_token");
if (Common.isEmpty(token)) {
log.info(result);
throw new CheckedException("获取微信token失败");
}
redisTemplate.opsForValue().set(tokenKey, token);
redisTemplate.expire(tokenKey, 3600, TimeUnit.SECONDS);
return token;
}
/**
* @param
* @Author: fxj
* @Date: 2025-01-07
* @Description: 移除微信accossToken
* @return: java.lang.String
**/
public void removeAccessToken() {
redisTemplate.delete(CacheConstants.WX_ACCOSS_TOKEN);
}
/**
* 移除微信accossToken
* @Author: fxj
* @Date: 2025-01-07
* @param agentId
* @return
**/
public void removeAccessToken(String agentId) {
if(Common.isNotNull(agentId)){
redisTemplate.delete( CacheConstants.WX_ACCOSS_TOKEN.concat(agentId));
}else{
removeAccessToken();
}
}
/**
* @param
* @Author: fxj
* @Date: 2025-01-07
* @Description: 移除微信accossToken
* @return: java.lang.String
**/
public void removeJsapiTicket() {
redisTemplate.delete(CacheConstants.WX_JSAPI_TICKET);
}
/**
* @param restTemplate
* @param access_token
* @param timeMillis 时间戳
* @param random 随机数
* @param url 网页url
* @Author: fxj
* @Date: 2025-01-07
* @Description: 微信jsapi签名
* @return: java.lang.String
**/
public String getSign(RestTemplate restTemplate, String access_token, long timeMillis, String random, String url) {
Object wxJsapiTicket = redisTemplate.opsForValue().get(CacheConstants.WX_JSAPI_TICKET);
log.info("wxJsapiTicket="+JSON.toJSONString(wxJsapiTicket));
String ticket = null;
if (wxJsapiTicket != null) {
log.info("wxJsapiTicket222="+JSON.toJSONString(wxJsapiTicket));
ticket = String.valueOf(wxJsapiTicket);
} else {
String getJsapiTicket = String.format(SecurityConstants.WX_JSAPI_TICKET_URL, access_token);
log.info("wxJsapiTicket111="+JSON.toJSONString(wxJsapiTicket));
String jsapiTicketresult = restTemplate.getForObject(getJsapiTicket, String.class);
if (Common.isEmpty(jsapiTicketresult)) {
throw new CheckedException("获取微信JsapiTicket失败");
}
JSONObject jsonObject = JSON.parseObject(jsapiTicketresult);
if (StringUtils.equals(accossTokenInvliad, jsonObject.getString("errcode"))) {
log.info(jsonObject.toJSONString());
removeAccessToken();
throw new CheckedException("accoss_token失效");
}
ticket = jsonObject.getString("ticket");
if (Common.isEmpty(ticket)) {
log.info(jsonObject.toJSONString());
removeJsapiTicket();
throw new CheckedException("获取微信ticket失败");
}
redisTemplate.opsForValue().set(CacheConstants.WX_JSAPI_TICKET, ticket);
redisTemplate.expire(CacheConstants.WX_JSAPI_TICKET, 3600, TimeUnit.SECONDS);
}
//拼接字符串并用sha1加密
String signString = new StrBuilder("jsapi_ticket=").append(ticket).append("&noncestr=").append(random).append("&timestamp=")
.append(timeMillis).append("&url=").append(url).toString();
String sign = EncryptUtil.getSha1(signString);
log.info("signString=" + signString);
log.info("sign=" + sign);
if (Common.isEmpty(sign)) {
log.info("signString=" + signString);
throw new CheckedException("加密失败");
}
return sign;
}
/**
* @param restTemplate
* @param requestMap 请求内容
* @Author: fxj
* @Date: 2025-01-07
* @Description: 发送卡片消息
* @return: java.lang.String
**/
public boolean sendTextCard(RestTemplate restTemplate, Map<String, Object> requestMap) {
// 必须加上header说明
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
Gson gson = new Gson();
log.info(gson.toJson(requestMap));
HttpEntity<String> requestEntity = new HttpEntity<>(gson.toJson(requestMap), headers);
String accessToken = getAccessToken(restTemplate);
ResponseEntity<String> responseEntity = restTemplate.postForEntity(String.format(SecurityConstants.WX_SEND_MESSAGE, accessToken), requestEntity, String.class);
log.info(JSON.toJSONString(responseEntity));
JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(responseEntity));
JSONObject jsonBody = jsonObject.getJSONObject("body");
if (jsonBody != null) {
String errcode = jsonBody.getString("errcode");
if (StringUtils.equals(accossTokenInvliad, errcode)) {
//删除accossToken缓存
removeAccessToken();
return false;
}
if (!StringUtils.equals(CommonConstants.ZERO_STRING, errcode)) { //非正常,则打印错误日志
log.info(jsonObject.toJSONString());
}
} else {
log.info(jsonObject.toJSONString());
}
return true;
}
/**
* @param restTemplate
* @param requestMap 请求内容
* @Author: wangan
* @Date: 2020/7/30
* @Description: 发送卡片消息
* @return: java.lang.String
**/
/*public boolean sendAppTextCard(RestTemplate restTemplate, Map<String, Object> requestMap) {
// 必须加上header说明
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
Gson gson = new Gson();
log.info("发企业微信===请求:{}", gson.toJson(requestMap));
HttpEntity<String> requestEntity = new HttpEntity<>(gson.toJson(requestMap), headers);
String accessToken = getAppAccessToken(restTemplate);
ResponseEntity<String> responseEntity = restTemplate.postForEntity(String.format(SecurityConstants.WX_SEND_MESSAGE, accessToken), requestEntity, String.class);
log.info("发企业微信===返回:{}",JSON.toJSONString(responseEntity));
JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(responseEntity));
JSONObject jsonBody = jsonObject.getJSONObject("body");
if (jsonBody != null) {
String errcode = jsonBody.getString("errcode");
if (StringUtils.equals(accossTokenInvliad, errcode)) {
//删除accossToken缓存
removeAccessToken();
return false;
}
if (!StringUtils.equals(CommonConstants.ZERO_STRING, errcode)) { //非正常,则打印错误日志
log.info(jsonObject.toJSONString());
}
} else {
log.info(jsonObject.toJSONString());
}
return true;
}*/
/**
* 功能描述: 获取微信accos_token
* @Param: [restTemplate]
* @Return: java.lang.String
* @Author: zhouyang
* @Date: 2021/10/12 15:27
*/
/*public String getAppAccessToken(RestTemplate restTemplate) {
return this.getToken(restTemplate, CacheConstants.WX_ACCOSS_TOKEN, appcorpsecret);
}*/
/**
* 功能描述: 获取token
* @Param: [restTemplate, tokenKey, corpsecretKey]
* @Return: java.lang.String
* @Author: zhouyang
* @Date: 2021/10/12 15:25
*/
/*public String getAppToken(RestTemplate restTemplate,String tokenKey, String corpsecretKey) {
Object wxToken = redisTemplate.opsForValue().get(tokenKey);
if (wxToken != null) {
return String.valueOf(wxToken);
}
String requestTokenUrl = String.format(SecurityConstants.WX_GET_ACCOSS_TOKEN, appCorpid, corpsecretKey);
String result = restTemplate.getForObject(requestTokenUrl, String.class);
if (Common.isEmpty(result)) {
throw new CheckedException("微信授权失败");
}
String token = JSON.parseObject(result).getString("access_token");
if (Common.isEmpty(token)) {
log.info(result);
throw new CheckedException("获取微信token失败");
}
redisTemplate.opsForValue().set(tokenKey, token);
redisTemplate.expire(tokenKey, 3600, TimeUnit.SECONDS);
return token;
}*/
/**
* 功能描述: 获取token
* @Param: [restTemplate, tokenKey, corpsecretKey]
* @Return: java.lang.String
* @Author: zhouyang
* @Date: 2021/10/12 15:25
*/
/*public String isInVisibleRange(String account) {
RestTemplate restTemplate = new RestTemplate();
log.info("查询用户是否在可见范围内===请求:{}");
String accessToken = getAppAccessToken(restTemplate);
ResponseEntity<String> responseEntity = restTemplate.getForEntity(String.format(SecurityConstants.WX_IS_IN_VISIBLE_RANGE, accessToken,account), String.class);
log.info("查询用户是否在可见范围内===返回:{}",JSON.toJSONString(responseEntity));
JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(responseEntity));
JSONObject jsonBody = jsonObject.getJSONObject("body");
String errcode ="";
if (jsonBody != null) {
errcode = jsonBody.getString("errcode");
if (!StringUtils.equals(CommonConstants.ZERO_STRING, errcode)) { //非正常,则打印错误日志
log.info(jsonObject.toJSONString());
}
} else {
log.info(jsonObject.toJSONString());
}
return errcode;
}*/
}
package com.yifu.cloud.plus.v1.yifu.auth.filter;
import com.alibaba.fastjson.JSONObject;
import com.yifu.cloud.plus.v1.yifu.auth.config.WxConfig;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.SecurityConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.util.Common;
import com.yifu.cloud.plus.v1.yifu.common.security.token.WxAuthenticationToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
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 org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @param
* @Author: wangan
* @Date: 2020/7/20
* @Description: 微信登录
* @return:
**/
@Slf4j
public class WxLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private static final String SPRING_SECURITY_RESTFUL_LOGIN_URL = "/oauth/wxLogin";
private boolean postOnly = true;
private RestTemplate restTemplate = new RestTemplate();
@Autowired
private WxConfig wxConfig;
public WxLoginAuthenticationFilter() {
super(new AntPathRequestMatcher(SPRING_SECURITY_RESTFUL_LOGIN_URL, "POST"));
}
/**
*
* @Author pwang
* @Date 2021-07-23 16:37
* code 对应的code 必传
* cleintType 应用类型 非必传applets表示小程序不传调公众号号接口
* @return agentId 对应的agentId 非必传 不传查配置的默认应用信息(合同审批)
**/
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
AbstractAuthenticationToken authRequest;
String code = obtainParameter(request, "code");
if (Common.isEmpty(code)) {
throw new AuthenticationServiceException("未获取到授权码");
}
// String result = restTemplate.getForObject(String.format(SecurityConstants.WX_GET_ACCOSS_TOKEN, wxConfig.getCorpid(), wxConfig.getCorpsecret()), String.class);
// if (Common.isEmpty(result)) {
// throw new AuthenticationServiceException("微信授权失败");
// }
// String access_token = JSONObject.parseObject(result).getString("access_token");
// if (Common.isEmpty(access_token)) {
// throw new AuthenticationServiceException("微信授权失败");
// }
String agentId = obtainParameter(request, "agentId");
String access_token = null;
if (Common.isNotNull(agentId)) {
//根据agentId确定corpsecret
access_token = wxConfig.getAccessToken(restTemplate,agentId);
}else{
//兼容老版本写法取默认的corpsecret
access_token = wxConfig.getAccessToken(restTemplate);
}
// log.info("access_token={}", access_token);
String cleintType = obtainParameter(request, "cleintType");
String userResult = null;
if(Common.isNotNull(cleintType)){
//指定应用类型按参数判断
if(cleintType.equals(SecurityConstants.WX_APPLETS_KEY)){
//小程序
userResult = restTemplate.getForObject(SecurityConstants.WX_APPLETS_GET_USER_ID, String.class, access_token, code);
}else{
throw new AuthenticationServiceException("无此应用类型");
}
}else{
//兼容老逻辑 查公众号接口
userResult = restTemplate.getForObject(SecurityConstants.WX_GET_USER_ID, String.class, access_token, code);
}
if (Common.isEmpty(userResult)) {
log.info(userResult);
throw new AuthenticationServiceException("获取企业微信用户失败");
}
// log.info(JSONObject.toJSONString(userResult)); {"errcode":40014,"errmsg":"invalid access_token"}
JSONObject jsonObject = JSONObject.parseObject(userResult);
if (StringUtils.equals(wxConfig.getAccossTokenInvliad(), jsonObject.getString(wxConfig.getErrcode()))) {
wxConfig.removeAccessToken(agentId);
log.info(userResult);
throw new AuthenticationServiceException("无效的微信accoss_token");
}else if(!StringUtils.equals(wxConfig.getAccossTokenSuccess(), jsonObject.getString(wxConfig.getErrcode()))){
wxConfig.removeAccessToken(agentId);
log.info(userResult);
throw new AuthenticationServiceException("企业微信认证错误,企业微信返回错误码:"+jsonObject.getString(wxConfig.getErrcode())+"(错误码地址:https://work.weixin.qq.com/api/doc/90001/90148/90455)");
}
String UserId = null;
if(Common.isNotNull(cleintType)){
//指定应用类型按参数判断
if(cleintType.equals(SecurityConstants.WX_APPLETS_KEY)){
//小程序
UserId = jsonObject.getString("userid");
}else{
throw new AuthenticationServiceException("无此应用类型");
}
}else{
//兼容老逻辑 查公众号接口
UserId = jsonObject.getString("UserId");
}
if (Common.isEmpty(UserId)) {
throw new AuthenticationServiceException("微信用户匹配失败");
}
//log.info("获取企业微信用户====UserId={}", UserId);
String principal;
String credentials = null;
String wxUserId = UserId; //企业微信账号
principal = wxUserId;
principal = principal.trim();
authRequest = new WxAuthenticationToken(principal, credentials);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
Authentication authenticate = this.getAuthenticationManager().authenticate(authRequest);
return authenticate;
}
private void setDetails(HttpServletRequest request,
AbstractAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
private String obtainParameter(HttpServletRequest request, String parameter) {
String result = request.getParameter(parameter);
return result == null ? "" : result;
}
}
// //企业微信登录
// String wxUserId="1111";
// principal = wxUserId;
// credentials = null;
// authRequest = new WxAuthenticationToken(principal, credentials);
//人力云调用企业微信进行网页授权
// String result = restTemplate.getForObject("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=wwbcb090af0dfe50e5&corpsecret=R0nKkvsY-oF41fuQvUXZ-kFG3_g_Ce0bpZt6mByx524", String.class);
// if (Common.isEmpty(result)) {
// throw new AuthenticationServiceException("微信授权失败");
// }
// String access_token = JSONObject.parseObject(result).getString("access_token");
// if(Common.isEmpty(access_token)){
// throw new AuthenticationServiceException("微信授权失败");
// }
// String userResult = restTemplate.getForObject("https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token={ACCESS_TOKEN}&code=CODE", String.class, access_token);
//// if (Common.isEmpty(userResult)) {
//// throw new AuthenticationServiceException("获取企业微信用户失败");
//// }
//// String UserId = JSONObject.parseObject(userResult).getString("UserId");
// String UserId="1111";
// if(Common.isEmpty(UserId)){
// throw new AuthenticationServiceException("未查询到企业微信用户对应的hr系统用户");
// }
// R<SysUser> remoteSysUerR = remoteUserService.getSimpleUserByWxUserId(UserId, SecurityConstants.FROM_IN);
// if (remoteSysUerR == null) {
// throw new RuntimeException("调用用户服务失败");
// }
// if (CommonConstants.SUCCESS != remoteSysUerR.getCode()) {
// throw new RuntimeException("调用用户服务返回失败");
// }
// SysUser sysUser = remoteSysUerR.getData();
// if (sysUser == null) {
// throw new RuntimeException("未查询到用户信息");
// }
// //获取用户信息
// principal = sysUser.getUsername().trim();
// authRequest = new QrAuthenticationToken(principal, null);
//
// // Allow subclasses to set the "details" property
// setDetails(request, authRequest);
// return this.getAuthenticationManager().authenticate(authRequest);
...@@ -43,3 +43,8 @@ weixin: ...@@ -43,3 +43,8 @@ weixin:
redirectUri: https://www.ngrok.xiaomiqiu.cn/admin/hi redirectUri: https://www.ngrok.xiaomiqiu.cn/admin/hi
errorUri: /error errorUri: /error
callbackUri: https://www.ngrok.xiaomiqiu.cn/oauth/weixin/callback callbackUri: https://www.ngrok.xiaomiqiu.cn/oauth/weixin/callback
wx:
corpid: wwbcb090af0dfe50e5
corpsecret: kFG3_g_Ce0bpZt6mByx524
agentid: 1000009
authUrl: https://wx.worfu.com/auth/oauth/wxLogin
\ No newline at end of file
...@@ -37,3 +37,8 @@ spring: ...@@ -37,3 +37,8 @@ spring:
prefer-file-system-access: true prefer-file-system-access: true
suffix: .ftl suffix: .ftl
template-loader-path: classpath:/templates/ template-loader-path: classpath:/templates/
wx:
corpid: wwbcb090af0dfe50e5
corpsecret: kFG3_g_Ce0bpZt6mByx524
agentid: 1000009
authUrl: https://wx.worfu.com/auth/oauth/wxLogin
\ No newline at end of file
...@@ -49,3 +49,9 @@ spring: ...@@ -49,3 +49,9 @@ spring:
prefer-file-system-access: true prefer-file-system-access: true
suffix: .ftl suffix: .ftl
template-loader-path: classpath:/templates/ template-loader-path: classpath:/templates/
wx:
corpid: wwbcb090af0dfe50e5
corpsecret: kFG3_g_Ce0bpZt6mByx524
agentid: 1000009
authUrl: https://wx.worfu.com/auth/oauth/wxLogin
\ No newline at end of file
...@@ -200,4 +200,6 @@ public interface CacheConstants { ...@@ -200,4 +200,6 @@ public interface CacheConstants {
* C端发验证码前缀 * C端发验证码前缀
*/ */
public static final String MVP_TOC_PHONE_CODE_PREFIX = "MVP_TOC_PHONE_CODE_"; public static final String MVP_TOC_PHONE_CODE_PREFIX = "MVP_TOC_PHONE_CODE_";
public static final String WX_JSAPI_TICKET = "WX_JSAPI_TICKET";
} }
...@@ -157,4 +157,42 @@ public interface SecurityConstants { ...@@ -157,4 +157,42 @@ public interface SecurityConstants {
// 获取审核详情 // 获取审核详情
String WX_GET_APPROVAL_DETAIL = "https://qyapi.weixin.qq.com/cgi-bin/oa/getapprovaldetail?access_token={1}"; String WX_GET_APPROVAL_DETAIL = "https://qyapi.weixin.qq.com/cgi-bin/oa/getapprovaldetail?access_token={1}";
/**
* @Author: wangan
* @Date: 2020/7/29
* @Description: 企业微信获取企业的jsapi_ticket 用于签名
* @return:
**/
String WX_JSAPI_TICKET_URL = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=%s";
/**
* @Author: wangdayu
* @Date: 2024/3/25
* @Description: 判断是否在应用可见范围内
* @return:
**/
String WX_IS_IN_VISIBLE_RANGE= "https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=%s&userid=%s";
/**
* 微信小程序
* @Author pwang
* @Date 2021-07-23 16:33
* @param null
* @return
**/
String WX_APPLETS_KEY = "applets";
/**
* @Author: pwang
* @Date: 2021/7/23
* @Description: 企业微信小程序获取用户userId
* @return:
**/
String WX_APPLETS_GET_USER_ID = "https://qyapi.weixin.qq.com/cgi-bin/miniprogram/jscode2session?access_token={1}&js_code={2}&grant_type=authorization_code";
/**
* @Author: wangan
* @Date: 2020/7/29
* @Description: 企业微信获取用户userId
* @return:
**/
String WX_GET_USER_ID = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token={1}&code={2}";
} }
package com.yifu.cloud.plus.v1.yifu.common.core.util;
import sun.misc.BASE64Encoder;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
/**
* 采用MD5加密
*
* @author shixc
* @datetime 2016-04-12
*/
public class EncryptUtil {
/***
* MD5加密 生成32位md5码
* @param inStr 待加密字符串
* @return 返回32位md5码
* @throws UnsupportedEncodingException
*/
public static String md5Encode(String inStr) throws UnsupportedEncodingException {
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (Exception e) {
System.out.println(e.toString());
e.printStackTrace();
return "";
}
byte[] byteArray = inStr.getBytes("UTF-8");
byte[] md5Bytes = md5.digest(byteArray);
StringBuffer hexValue = new StringBuffer();
for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
public static String base64Encoder(String src) throws UnsupportedEncodingException {
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(src.getBytes("UTF-8"));
}
/**
* 测试主函数
*
* @param args
* @throws Exception
*/
public static void main(String args[]) throws Exception {
String str = new String("");
System.out.println("原始:" + str);
System.out.println("MD5后:" + md5Encode(str));
}
/**
* @param str
* @Author: wangan
* @Date: 2020/7/29
* @Description: sha1加密
* @return: java.lang.String
**/
public static String getSha1(String str) {
if (str == null || str.length() == 0) {
return null;
}
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
mdTemp.update(str.getBytes("UTF-8"));
byte[] md = mdTemp.digest();
int j = md.length;
char buf[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
buf[k++] = hexDigits[byte0 & 0xf];
}
return new String(buf);
} catch (Exception e) {
return null;
}
}
}
package com.yifu.cloud.plus.v1.yifu.common.security.component;
import com.yifu.cloud.plus.v1.yifu.common.security.token.WxAuthenticationToken;
import lombok.extern.slf4j.Slf4j;
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.core.userdetails.UsernameNotFoundException;
/**
* 手机验证码登陆
*/
@Slf4j
public class WxAuthenticationProvider extends MyAbstractUserDetailsAuthenticationProvider {
private UserDetailsService userDetailsService;
@Override
protected void additionalAuthenticationChecks(UserDetails var1, Authentication authentication) throws AuthenticationException {
// if(authentication.getCredentials() == null) {
// this.logger.debug("Authentication failed: no credentials provided");
// throw new BadCredentialsException(this.messages.getMessage("PhoneAuthenticationProvider.badCredentials", "Bad credentials"));
// }
}
@Override
protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
WxAuthenticationToken result = new WxAuthenticationToken(principal, authentication.getCredentials(), user.getAuthorities());
result.setDetails(authentication.getDetails());
return result;
}
@Override
protected UserDetails retrieveUser(String wxUserName, Authentication authentication) throws AuthenticationException {
UserDetails loadedUser;
// String presentedPassword = authentication.getCredentials().toString();
//
// // 验证码验证,调用公共服务查询 key 为authentication.getPrincipal()的value, 并判断其与验证码是否匹配
// String code = redisUtil.get(wxUserName).toString();
// if(!code.equals(presentedPassword)){
// this.logger.debug("Authentication failed: verifyCode does not match stored value");
// throw new BadCredentialsException(this.messages.getMessage("PhoneAuthenticationProvider.badCredentials", "Bad verifyCode"));
// }
try {
loadedUser = this.getUserDetailsService().loadUserByUsername(wxUserName);
} catch (UsernameNotFoundException var6) {
throw var6;
} catch (Exception var7) {
log.info("WxAuthenticationProvider>>>>>>",var7);
throw new InternalAuthenticationServiceException(var7.getMessage(), var7);
}
if(loadedUser == null) {
throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
} else {
return loadedUser;
}
}
@Override
public boolean supports(Class<?> authentication) {
return WxAuthenticationToken.class.isAssignableFrom(authentication);
}
public UserDetailsService getUserDetailsService() {
return userDetailsService;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
}
package com.yifu.cloud.plus.v1.yifu.common.security.service;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.yifu.cloud.plus.v1.yifu.admin.api.dto.UserInfo;
import com.yifu.cloud.plus.v1.yifu.admin.api.entity.SysUser;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.CommonConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.SecurityConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.ServiceNameConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.exception.CheckedException;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
import com.yifu.cloud.plus.v1.yifu.common.core.vo.YifuUser;
import com.yifu.cloud.plus.v1.yifu.common.dapr.config.DaprUpmsProperties;
import com.yifu.cloud.plus.v1.yifu.common.dapr.util.HttpDaprUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* 手机验证码登录()
*/
@Slf4j
@EnableConfigurationProperties(DaprUpmsProperties.class)
@Service
@AllArgsConstructor
public class WxUserDetailService implements UserDetailsService {
private CacheManager cacheManager;
private final DaprUpmsProperties daprUpmsProperties;
/**
* 手机验证码登录
*
* @param wxUserName 微信用户名
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String wxUserName) throws UsernameNotFoundException {
Cache cache = cacheManager.getCache(ServiceNameConstants.UMPS_SERVICE + "_user_details_wx");
if (null != cache && null != cache.get(wxUserName)) {
return (YifuUser) cache.get(wxUserName).get();
}
//根据手机号获取用户
R<UserInfo> result = HttpDaprUtil.invokeMethodGet(daprUpmsProperties.getAppUrl(),daprUpmsProperties.getAppId(), "/user/getInfoByWxUsername", "?username="+wxUserName, UserInfo.class, SecurityConstants.FROM_IN);
UserDetails userDetails = getUserDetails(result);
if (cache == null) {
throw new CheckedException("缓存为空");
}
cache.put(wxUserName, userDetails);
return userDetails;
}
/**
* 构建userdetails
*
* @param result 用户信息
* @return
*/
private UserDetails getUserDetails(R<UserInfo> result) {
if (result == null || result.getData() == null) {
throw new UsernameNotFoundException("用户不存在");
}
UserInfo info = result.getData();
Set<String> dbAuthsSet = new HashSet<>();
if (ArrayUtil.isNotEmpty(info.getRoles())) {
// 获取角色
Arrays.stream(info.getRoles()).forEach(role -> dbAuthsSet.add(SecurityConstants.ROLE + role));
// 获取资源
dbAuthsSet.addAll(Arrays.asList(info.getPermissions()));
}
Collection<? extends GrantedAuthority> authorities = AuthorityUtils
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
SysUser user = info.getSysUser();
// 构造security用户
return new YifuUser(user.getUserId(), user.getDeptId(),user.getDeptName(), user.getUsername(),
user.getNickname(),user.getSystemFlag(), SecurityConstants.BCRYPT + user.getPassword(),
user.getPhone(), true, true, true,
StrUtil.equals(user.getLockFlag(), CommonConstants.STATUS_NORMAL),
user.getUserGroup(),authorities, user.getLdapDn(),info.getClientRoleMap(),
info.getSettleIdList(),user.getType());
}
}
package com.yifu.cloud.plus.v1.yifu.common.security.token;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
/**
* 手机验证码token
*/
public class WxAuthenticationToken extends MyAuthenticationToken {
public WxAuthenticationToken(Object principal, Object credentials) {
super(principal, credentials);
}
public WxAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}
}
...@@ -5,5 +5,6 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ ...@@ -5,5 +5,6 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.yifu.cloud.plus.v1.yifu.common.security.component.YifuTokenStoreAutoConfiguration,\ com.yifu.cloud.plus.v1.yifu.common.security.component.YifuTokenStoreAutoConfiguration,\
com.yifu.cloud.plus.v1.yifu.common.security.component.YifuTokenStoreAutoCleanSchedule,\ com.yifu.cloud.plus.v1.yifu.common.security.component.YifuTokenStoreAutoCleanSchedule,\
com.yifu.cloud.plus.v1.yifu.common.security.component.YifuSecurityMessageSourceConfiguration,\ com.yifu.cloud.plus.v1.yifu.common.security.component.YifuSecurityMessageSourceConfiguration,\
com.yifu.cloud.plus.v1.yifu.common.security.service.WxUserDetailService,\
com.yifu.cloud.plus.v1.yifu.common.security.exception.GlobalExceptionHandler com.yifu.cloud.plus.v1.yifu.common.security.exception.GlobalExceptionHandler
...@@ -121,7 +121,16 @@ public class UserController { ...@@ -121,7 +121,16 @@ public class UserController {
} }
return userService.getUserInfo(user); return userService.getUserInfo(user);
} }
@SysLog("登录获取账号信息异常")
@Inner
@GetMapping("/getInfoByWxUsername")
public UserInfo infoWxAPI(@RequestParam(required = true, name = "username") String username) {
SysUser user = userService.getOne(Wrappers.<SysUser>query().lambda().eq(SysUser::getWxMessage, username).last(CommonConstants.LAST_ONE_SQL));
if (null == user){
throw new RuntimeException("未获取到用户:"+ username);
}
return userService.getUserInfo(user);
}
/** /**
* 根据部门id,查询对应的用户 id 集合 * 根据部门id,查询对应的用户 id 集合
* *
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment