add wechat login and share

main
Revers 10 months ago
parent 53b8c2d52f
commit 59dd76af7e

@ -21,6 +21,8 @@ public enum StatusCode implements IErrorCode {
USER_SENSITIVE_INFO(10008, "不能提交敏感信息"), USER_SENSITIVE_INFO(10008, "不能提交敏感信息"),
APPLE_lOGIN_FAILED(10009, "apple id登录失败"), APPLE_lOGIN_FAILED(10009, "apple id登录失败"),
WECHAT_lOGIN_FAILED(10011, "微信 id登录失败"),
APPLEID_MOBILE_UNBINDED(10010,"请先绑定appleid和手机号"), APPLEID_MOBILE_UNBINDED(10010,"请先绑定appleid和手机号"),

@ -6,6 +6,10 @@ public class Constants {
public static final String REDIS_KEY_IMAGE_CHECK_CODE = "redis_key_image_check_code_"; public static final String REDIS_KEY_IMAGE_CHECK_CODE = "redis_key_image_check_code_";
public static final String REDIS_KEY_MOBILE_CHECK_CODE = "redis_key_mobile_check_code_"; public static final String REDIS_KEY_MOBILE_CHECK_CODE = "redis_key_mobile_check_code_";
public static final String REDIS_KEY_WECHAT_TOKEN = "redis_key_wechat_token_byopenid_";
public static final String REDIS_KEY_WECHAT_REFRESH_TOKEN = "redis_key_wechat_refresh_token_byopenid_";
public static final String REDIS_KEY_USER_COLLECT_JOURNAL = "redis_key_user_collect_journal_"; public static final String REDIS_KEY_USER_COLLECT_JOURNAL = "redis_key_user_collect_journal_";
public static final String REDIS_KEY_PAGE_JOURNAL_RESPONSE_DTO = "redis_key_page_journal_response_dto___"; public static final String REDIS_KEY_PAGE_JOURNAL_RESPONSE_DTO = "redis_key_page_journal_response_dto___";
@ -69,4 +73,12 @@ public class Constants {
public static final Integer LENGTH_512 = 512; public static final Integer LENGTH_512 = 512;
public static final String IP_LOCATION_CHINA = "中国"; public static final String IP_LOCATION_CHINA = "中国";
public static final String HTTPS_API_WEIXIN_QQ_COM_SNS_OAUTH2_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token";
public static final String HTTPS_API_WEIXIN_QQ_COM_SNS_OAUTH2_REFRESH_TOKEN = "https://api.weixin.qq.com/sns/oauth2/refresh_token";
public static final String HTTPS_API_WEIXIN_QQ_COM_SNS_AUTH = "https://api.weixin.qq.com/sns/auth";
public static final String HTTPS_API_WEIXIN_QQ_JSAPI = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";
} }

@ -0,0 +1,65 @@
package com.luoo.user.controller;
import annotation.GlobalInterceptor;
import annotation.VerifyParam;
import api.Result;
import api.StatusCode;
import com.luoo.user.dto.request.WeChatBindReq;
import com.luoo.user.dto.request.WeChatLoginReq;
import com.luoo.user.dto.request.WeChatShareReq;
import com.luoo.user.service.WeChatAouth2Service;
import dto.UserLoginDto;
import enums.RequestFrequencyTypeEnum;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import controller.BaseController;
import util.JwtUtil;
import javax.servlet.http.HttpServletRequest;
/**
* @author Revers.
* @date 2024/03/21 10:05
**/
@CrossOrigin
@RestController
@RequestMapping("/wechat")
@Api(tags = "WechatLoginController")
public class WeChatAouth2Controller extends BaseController {
@Autowired
private WeChatAouth2Service weChatAouth2Service;
@ApiOperation(value = "分享获取微信token", notes = "成功后返回token,不成功返回null")
@PostMapping("/getShareAccessToken")
public Result getShareAccessToken(@RequestBody WeChatShareReq weChatShareReq) {
return weChatAouth2Service.getShareToken(weChatShareReq);
}
@ApiOperation(value = "微信登录/注册",notes = "微信登录/注册,成功后返回authorization")
@PostMapping("/login")
@GlobalInterceptor(frequencyType = RequestFrequencyTypeEnum.HOUR, requestFrequencyThreshold = 12)
public Result<String> getLoginAccessToken(HttpServletRequest request,
@RequestBody @VerifyParam WeChatLoginReq loginReq){
return weChatAouth2Service.loginOrRegister(loginReq,getIpAddr(request));
}
@ApiOperation(value = "绑定手机号",notes = "绑定手机号")
@PostMapping("/bindPhoneNumber")
@GlobalInterceptor(frequencyType = RequestFrequencyTypeEnum.HOUR, requestFrequencyThreshold = 12)
public Result bindPhoneNumber(@RequestHeader(value = "Authorization", required = true) String authorization,
@RequestBody @VerifyParam WeChatBindReq weChatBindReq){
UserLoginDto user = new JwtUtil().getUserLoginDto(authorization);
if(user == null)
return Result.failed(StatusCode.UNAUTHORIZED);
return weChatAouth2Service.bindPhoneNumber(user,weChatBindReq);
}
}

@ -3,6 +3,7 @@ package com.luoo.user.dao;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import com.luoo.user.pojo.User;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@ -17,7 +18,7 @@ import client.vo.SimpleUser;
public interface UserInfoDao extends JpaRepository<UserInfo, String>, JpaSpecificationExecutor<UserInfo> { public interface UserInfoDao extends JpaRepository<UserInfo, String>, JpaSpecificationExecutor<UserInfo> {
public UserInfo findByMobile(String mobile); public UserInfo findByMobile(String mobile);
public UserInfo findUserInfoByWxId(String wxId);
public UserInfo findByAppleId(String appleId); public UserInfo findByAppleId(String appleId);
@Modifying @Modifying
@Query(value = "update tb_user_info set follow_count=follow_count+? where id = ?", nativeQuery = true) @Query(value = "update tb_user_info set follow_count=follow_count+? where id = ?", nativeQuery = true)

@ -0,0 +1,28 @@
package com.luoo.user.dto.request;
import annotation.VerifyParam;
import enums.VerifyRegexEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
/**
* @author Revers.
* @date 2024/03/25 17:56
**/
@Getter
@Setter
@ApiModel
public class WeChatBindReq implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(name = "mobile", value = "手机号", required = true)
@VerifyParam(required = true, regex = VerifyRegexEnum.MOBILE)
private String mobile;
}

@ -0,0 +1,35 @@
package com.luoo.user.dto.request;
import annotation.VerifyParam;
import enums.VerifyRegexEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
/**
* @author Revers.
* @date 2024/03/25 17:26
**/
@Getter
@Setter
@ApiModel
public class WeChatLoginReq implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(name = "code", value = "code码", required = true)
@VerifyParam(required = true)
String code;
@ApiModelProperty(name = "deviceId", value = "设备id", required = true)
@VerifyParam(required = true)
private String deviceId;
@ApiModelProperty(name = "deviceBrand", value = "设备品牌", required = false)
String deviceBrand;
}

@ -0,0 +1,14 @@
package com.luoo.user.dto.request;
import lombok.Data;
/**
* @author Revers.
* @date 2024/03/25 19:21
**/
@Data
public class WeChatShareReq {
private String code;
}

@ -0,0 +1,15 @@
package com.luoo.user.dto.response;
import lombok.Data;
/**
* @author Revers.
* @date 2024/03/25 18:09
**/
@Data
public class Aouth2WeChatErrorResponse {
private Integer errcode;
private String errmsg;
}

@ -0,0 +1,17 @@
package com.luoo.user.dto.response;
import lombok.Data;
/**
* @author Revers.
* @date 2024/03/26 21:45
**/
@Data
public class Aouth2WeChatJSAPIResponse {
private Integer errcode;
private String errmsg;
private String ticket;
private Integer expires_in;
}

@ -0,0 +1,18 @@
package com.luoo.user.dto.response;
import lombok.Data;
/**
* @author Revers.
* @date 2024/03/21 10:15
**/
@Data
public class Aouth2WeChatTokenResponse {
private String access_token;
private Long expires_in;
private String refresh_token;
private String openid;
private String scope;
private String unionid;
}

@ -44,6 +44,11 @@ public class UserInfo implements Serializable {
*/ */
private String appleId; private String appleId;
/**
* id
*/
private String wxId;
/** /**
* *
*/ */

@ -10,6 +10,8 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import api.Result;
import api.StatusCode;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
@ -250,6 +252,49 @@ public class UserInfoService {
return Constants.TOKEN_PREFIX return Constants.TOKEN_PREFIX
+ jwtUtil.createJWT(userInfo.getId(), userInfo.getNickName(), Constants.TOKEN_ROLE_APP_USER, userInfo.getAvatar()); + jwtUtil.createJWT(userInfo.getId(), userInfo.getNickName(), Constants.TOKEN_ROLE_APP_USER, userInfo.getAvatar());
} }
public String wechatloginOrRegister(UserInfo loginUserInfo) {
UserInfo userInfo = userInfoDao.findUserInfoByWxId(loginUserInfo.getWxId());
if (null == userInfo) {
userInfo = loginUserInfo;
userInfo.setId(String.valueOf(idWorker.nextId()));
userInfo.setNickName("雀乐-" + NickNameUtil.getRandomNickName());
Date curDate = new Date();
userInfo.setJoinTime(curDate);
userInfo.setLastLoginTime(curDate);
userInfo.setStatus(UserStatusEnum.ENABLE.getStatus());
userInfo.setAvatar(Constants.DEFAULT_USER_AVATAR);
userInfo.setThumbnail(Constants.DEFAULT_USER_THUMBNAIL);
userInfo.setSignature(Constants.DEFAULT_USER_SIGNATURE);
} else {
userInfo.setLastUseDeviceId(loginUserInfo.getLastUseDeviceId());
userInfo.setLastUseDeviceBrand(loginUserInfo.getLastUseDeviceBrand());
userInfo.setLastLoginIp(loginUserInfo.getLastLoginIp());
}
if (!UserStatusEnum.ENABLE.getStatus().equals(userInfo.getStatus())) {
return null;
}
userInfo.setLastLoginTime(new Date());
userInfoDao.save(userInfo);
return Constants.TOKEN_PREFIX
+ jwtUtil.createJWT(userInfo.getId(), userInfo.getNickName(), Constants.TOKEN_ROLE_APP_USER, userInfo.getAvatar());
}
public Result setPhoneNumber(String userid, String phoneNumber){
UserInfo userbyId = userInfoDao.getById(userid);
if(userbyId == null)
return Result.failed(StatusCode.USER_INVALID_USER_ID);
userbyId.setMobile(phoneNumber);
UserInfo save = userInfoDao.save(userbyId);
if(save != null)
return Result.success();
return Result.failed();
}
public List<UserInfo> orderByField(List<String> idList) { public List<UserInfo> orderByField(List<String> idList) {
return userInfoDao.orderByField(idList); return userInfoDao.orderByField(idList);
} }

@ -0,0 +1,188 @@
package com.luoo.user.service;
import api.Result;
import api.StatusCode;
import com.alibaba.fastjson.JSON;
import com.luoo.user.dto.response.Aouth2WeChatJSAPIResponse;
import com.luoo.user.dto.request.WeChatBindReq;
import com.luoo.user.dto.request.WeChatLoginReq;
import com.luoo.user.dto.request.WeChatShareReq;
import com.luoo.user.dto.response.Aouth2WeChatErrorResponse;
import com.luoo.user.dto.response.Aouth2WeChatTokenResponse;
import com.luoo.user.pojo.UserInfo;
import constants.Constants;
import dto.UserLoginDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
/**
* @author Revers.
* @date 2024/03/25 15:00
**/
//{"openid":"o5arS6kwYVmRexHdhJgjir9toPdw",
// "access_token":"78_VdzZCtUYiA83SDirkIUdir0SqWeb_LyUeX7A9CrszGUvHOd5AmWHFnWM1hEDoXpGQGOhzdQ1g478Z1ZDV8cmpyiKhSKgfJVSiEaVHWTO1vo",
// "expires_in":7200,"refresh_token":"78__Vyvij1j4NBfeZ_KTVDaE6XvKnU2J-OtprQC8EfkSTJszq08E1lwI_Wa_dj48b30j4RxQdugRpu8RJ-6l42bJWl5XyoUuk-8J4UyF1nlfDg",
// "scope":"snsapi_base"}
@Slf4j
@Service
public class WeChatAouth2Service {
@Value("${oauth2.weixin.appid}")
private String appid;
@Value("${oauth2.weixin.secret}")
private String secret;
@Autowired
private UserInfoService userInfoService;
@Autowired
private RedisTemplate redisTemplate;
public Result getShareToken(WeChatShareReq weChatShareReq){
Aouth2WeChatTokenResponse accessToken = getAccessToken(weChatShareReq.getCode());
if(accessToken == null || accessToken.getAccess_token() == null )
return Result.failed("分享微信失败");
Aouth2WeChatJSAPIResponse aouth2WeChatJSAPIResponse = getjsapi(accessToken.getAccess_token());
if(aouth2WeChatJSAPIResponse == null || aouth2WeChatJSAPIResponse.getTicket() == null)
return Result.failed("分享微信失败");
return Result.success(aouth2WeChatJSAPIResponse.getTicket());
}
public Result loginOrRegister(WeChatLoginReq loginReq,String ipAddress){
Aouth2WeChatTokenResponse accessToken = getAccessToken(loginReq.getCode());
if(accessToken == null || accessToken.getAccess_token() == null){
return Result.failed(StatusCode.WECHAT_lOGIN_FAILED);
}
UserInfo loginUserInfo = new UserInfo();
loginUserInfo.setWxId(accessToken.getOpenid());
loginUserInfo.setLastUseDeviceId(loginReq.getDeviceId());
loginUserInfo.setLastUseDeviceBrand(loginReq.getDeviceBrand());
loginUserInfo.setLastLoginIp(ipAddress);
String token = userInfoService.wechatloginOrRegister(loginUserInfo);
return Result.success(token);
}
public Result bindPhoneNumber(UserLoginDto user, WeChatBindReq weChatBindReq){
return userInfoService.setPhoneNumber(user.getUserId(),weChatBindReq.getMobile());
}
public Aouth2WeChatTokenResponse getAccessToken(String code){
StringBuffer url = new StringBuffer();
url.append(Constants.HTTPS_API_WEIXIN_QQ_COM_SNS_OAUTH2_ACCESS_TOKEN);
url.append("?appid=").append(urlEncode(appid));
url.append("&secret=").append(urlEncode(secret));
url.append("&code=").append(urlEncode(code));
url.append("&grant_type=authorization_code");
String string = new String(get(url.toString()));
log.info("refreshAccessToken:" + string);
Aouth2WeChatTokenResponse aouth2WeChatTokenResponse = JSON.parseObject(string, Aouth2WeChatTokenResponse.class);
saveTokenToRedis(aouth2WeChatTokenResponse);
return aouth2WeChatTokenResponse;
}
public Aouth2WeChatTokenResponse refreshAccessToken(String refresh_token) {
StringBuffer url = new StringBuffer();
url.append(Constants.HTTPS_API_WEIXIN_QQ_COM_SNS_OAUTH2_REFRESH_TOKEN);
url.append("?appid=").append(urlEncode(appid));
url.append("&grant_type=").append("refresh_token");
url.append("&refresh_token=").append(urlEncode(refresh_token));
String string = new String(get(url.toString()));
log.info("refreshAccessToken:" + string);
Aouth2WeChatTokenResponse aouth2WeChatTokenResponse = JSON.parseObject(string, Aouth2WeChatTokenResponse.class);
saveTokenToRedis(aouth2WeChatTokenResponse);
return aouth2WeChatTokenResponse;
}
public Boolean isValidAccessToken(String access_token,String openid) {
StringBuffer url = new StringBuffer();
url.append(Constants.HTTPS_API_WEIXIN_QQ_COM_SNS_AUTH);
url.append("?access_token=").append(urlEncode(access_token));
url.append("&openid=").append(urlEncode(openid));
String string = new String(get(url.toString()));
log.info("validAccessToken:" + string);
Aouth2WeChatErrorResponse dto = JSON.parseObject(string, Aouth2WeChatErrorResponse.class);
if(dto.getErrcode() == 0)
return true;
return false;
}
private Aouth2WeChatJSAPIResponse getjsapi(String access_token) {
StringBuffer url = new StringBuffer();
url.append(Constants.HTTPS_API_WEIXIN_QQ_JSAPI);
url.append("?access_token=").append(urlEncode(access_token));
url.append("&type=").append("jsapi");
String string = new String(get(url.toString()));
log.info("jsapi:" + string);
Aouth2WeChatJSAPIResponse aouth2WeChatJSAPIResponse = JSON.parseObject(string, Aouth2WeChatJSAPIResponse.class);
return aouth2WeChatJSAPIResponse;
}
private static String urlEncode(String str) {
String result = null;
try {
result = URLEncoder.encode(str, Consts.UTF_8.name());
} catch (UnsupportedEncodingException e) {
}
return result;
}
private static byte[] get(String url) {
byte[] res = null;
try {
HttpResponse response = new DefaultHttpClient().execute(new HttpGet(URI.create(url)));
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));
StringBuilder sb = new StringBuilder();
for (String temp = reader.readLine(); temp != null; temp = reader.readLine()) {
sb.append(temp);
}
res = sb.toString().trim().getBytes(StandardCharsets.UTF_8);
}
} catch (IOException e) {
}
return res;
}
private void saveTokenToRedis(Aouth2WeChatTokenResponse aouth2WeChatTokenResponse){
if(aouth2WeChatTokenResponse != null && aouth2WeChatTokenResponse.getOpenid() != null ){
if(aouth2WeChatTokenResponse.getAccess_token() != null)
redisTemplate.opsForValue().set(Constants.REDIS_KEY_WECHAT_TOKEN + aouth2WeChatTokenResponse.getOpenid(),aouth2WeChatTokenResponse.getAccess_token(),2, TimeUnit.HOURS);
if(aouth2WeChatTokenResponse.getRefresh_token() != null)
redisTemplate.opsForValue().set(Constants.REDIS_KEY_WECHAT_REFRESH_TOKEN + aouth2WeChatTokenResponse.getOpenid(),aouth2WeChatTokenResponse.getRefresh_token(),30, TimeUnit.DAYS);
}
}
}

@ -6,3 +6,8 @@ spring:
label: master label: master
uri: http://116.62.145.60:12000 uri: http://116.62.145.60:12000
# uri: http://127.0.0.1:12000 # uri: http://127.0.0.1:12000
oauth2:
weixin:
appid: wx667f580d1605650b
secret: 4bb42de98cb422d733ac7d50e7cade3b
Loading…
Cancel
Save