oauth-重写-引入jjwt和删除h5登录相关
This commit is contained in:
@@ -85,7 +85,9 @@ public class JwtUtils {
|
||||
//设置签名的秘钥
|
||||
.setSigningKey(key)
|
||||
//设置需要解析的jwt
|
||||
.parseClaimsJws(token).getBody();
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
return claims;
|
||||
}
|
||||
|
||||
@@ -105,7 +107,9 @@ public class JwtUtils {
|
||||
//设置签名的秘钥
|
||||
.setSigningKey(key)
|
||||
//设置需要解析的jwt
|
||||
.parseClaimsJws(token).getBody();
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
|
||||
if (claims.get("password").equals(account.getPassword())) {
|
||||
return true;
|
||||
@@ -113,4 +117,4 @@ public class JwtUtils {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,68 +0,0 @@
|
||||
package com.accompany.common.utils;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class JsonUtil {
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private static ObjectMapper objectMapperInner;
|
||||
|
||||
@PostConstruct
|
||||
private void setObjectMapperInner() {
|
||||
objectMapperInner = objectMapper;
|
||||
}
|
||||
|
||||
public static <T> T parseToClass(String jsonString, Class<T> tClass) {
|
||||
try {
|
||||
return objectMapperInner.readValue(jsonString, tClass);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String parseToString(Object o) {
|
||||
try {
|
||||
return objectMapperInner.writeValueAsString(o);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isJsonObject(String json) {
|
||||
try {
|
||||
return objectMapperInner.readTree(json).isObject();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isJsonArray(String json) {
|
||||
try {
|
||||
return objectMapperInner.readTree(json).isArray();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> List<T> parseArray(String json, Class<T> tClass) {
|
||||
try {
|
||||
return objectMapperInner.readValue(json, objectMapperInner.getTypeFactory()
|
||||
.constructCollectionType(List.class, tClass));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
package com.accompany.common.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Created by yuanyi on 2019/2/23.
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface H5Authorization {
|
||||
}
|
@@ -1,9 +1,8 @@
|
||||
package com.accompany.core.util;
|
||||
|
||||
import com.accompany.core.base.SpringContextHolder;
|
||||
import com.accompany.core.exception.ServiceException;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
|
||||
@@ -17,12 +16,7 @@ import java.io.IOException;
|
||||
public class JsonUtil {
|
||||
|
||||
private static ObjectMapper getMapper() {
|
||||
ObjectMapper bean = new ObjectMapper();
|
||||
//反序列化忽略多余属性
|
||||
bean.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
//如果是空对象的时候,不抛异常
|
||||
bean.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||
return bean;
|
||||
return SpringContextHolder.getBean(ObjectMapper.class);
|
||||
}
|
||||
|
||||
public static <T> T parseToClass(String jsonString, Class<T> tClass) {
|
||||
|
@@ -19,7 +19,7 @@ import com.accompany.business.service.user.UserBackpackService;
|
||||
import com.accompany.business.util.redenvelope.OpenRedEnvelopeUtil;
|
||||
import com.accompany.common.constant.Attach;
|
||||
import com.accompany.common.constant.Constant;
|
||||
import com.accompany.common.utils.JsonUtil;
|
||||
import com.accompany.core.util.JsonUtil;
|
||||
import com.accompany.core.base.SpringContextHolder;
|
||||
import com.accompany.core.model.Room;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@@ -16,7 +16,7 @@ import com.accompany.business.service.user.UsersService;
|
||||
import com.accompany.common.constant.Attach;
|
||||
import com.accompany.common.constant.Constant;
|
||||
import com.accompany.common.utils.DateTimeUtil;
|
||||
import com.accompany.common.utils.JsonUtil;
|
||||
import com.accompany.core.util.JsonUtil;
|
||||
import com.accompany.core.enumeration.I18nAlertEnum;
|
||||
import com.accompany.core.model.Users;
|
||||
import com.accompany.core.service.partition.PartitionInfoService;
|
||||
|
@@ -17,7 +17,7 @@ import com.accompany.common.constant.Attach;
|
||||
import com.accompany.common.constant.Constant;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import com.accompany.common.utils.DateTimeUtil;
|
||||
import com.accompany.common.utils.JsonUtil;
|
||||
import com.accompany.core.util.JsonUtil;
|
||||
import com.accompany.core.enumeration.I18nAlertEnum;
|
||||
import com.accompany.core.exception.ServiceException;
|
||||
import com.accompany.core.model.Users;
|
||||
|
@@ -1,287 +1,2 @@
|
||||
package com.accompany.business.service.room.impl;
|
||||
|
||||
import cn.hutool.core.date.StopWatch;
|
||||
import cn.hutool.core.lang.Pair;
|
||||
import com.accompany.business.constant.GenderEnum;
|
||||
import com.accompany.business.dto.QueueDTO;
|
||||
import com.accompany.business.dto.QueueListResponseDTO;
|
||||
import com.accompany.business.dto.QueueValueDTO;
|
||||
import com.accompany.business.enums.GenderArea;
|
||||
import com.accompany.business.service.room.*;
|
||||
import com.accompany.common.constant.Constant;
|
||||
import com.accompany.common.netease.ErBanNetEaseService;
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import com.accompany.core.exception.ServiceException;
|
||||
import com.accompany.core.model.Room;
|
||||
import com.accompany.core.util.JsonUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public class BlindDateMaxGiftValueServiceImpl implements BlindDateMaxGiftValueService {
|
||||
|
||||
@Autowired
|
||||
private BlindDateRoundConnectionService blindDateRoundConnectionService;
|
||||
|
||||
@Autowired
|
||||
private QueueService queueService;
|
||||
|
||||
@Autowired
|
||||
private QueryRoomService queryRoomService;
|
||||
|
||||
@Autowired
|
||||
private MicCharmService micCharmService;
|
||||
|
||||
@Autowired
|
||||
private BlindDateCapService blindDateCapService;
|
||||
|
||||
@Autowired
|
||||
private RoomService roomService;
|
||||
|
||||
@Autowired
|
||||
private RoomGiftValueService roomGiftValueService;
|
||||
|
||||
@Autowired
|
||||
private ErBanNetEaseService erBanNetEaseService;
|
||||
|
||||
@Autowired
|
||||
private RedissonClient redissonClient;
|
||||
|
||||
@Override
|
||||
public void trigger(Long roomUid) {
|
||||
StopWatch stw = new StopWatch("timer");
|
||||
stw.start();
|
||||
log.info("进入了相亲送社触发器");
|
||||
Room roomDTO = roomService.getRoomByUid(roomUid);
|
||||
if (roomDTO == null || roomDTO.getRoomModeType() != Constant.RoomModeType.OPEN_BLIND_DATE_MODE) {
|
||||
log.info("非相亲玩法");
|
||||
return;
|
||||
}
|
||||
|
||||
boolean locked = false;
|
||||
RLock lock = redissonClient.getLock(RedisKey.blind_room_max_gift_val_trigger_lock.getKey(roomUid.toString()));
|
||||
try {
|
||||
locked = lock.tryLock(5, TimeUnit.SECONDS);
|
||||
if (!locked){
|
||||
throw new ServiceException(BusiStatus.SERVERBUSY);
|
||||
}
|
||||
|
||||
long roomId = roomDTO.getRoomId();
|
||||
|
||||
// 发发起魅力值同步
|
||||
roomGiftValueService.pushCurrentGiftValues2Client(roomUid);
|
||||
|
||||
QueueListResponseDTO queueList = queryRoomService.queueList(roomDTO.getRoomId());
|
||||
List<QueueDTO> listDTO = queueList.getDesc().listDTO();
|
||||
|
||||
roomGiftValueService.upMicUpdateVipCache(roomDTO, listDTO);
|
||||
|
||||
log.info("相亲送社触发器" + JsonUtil.parseToString(listDTO));
|
||||
List<Long> micUserIds = listDTO.stream().map(dto -> dto.getValueObject().getUid()).collect(Collectors.toList());
|
||||
|
||||
// 获取到vip席位所在的麦位
|
||||
QueueDTO vipMic = listDTO.stream().filter(dto -> dto.getValueObject().getVipMic()).findFirst().orElse(null);
|
||||
|
||||
int sign = 0;
|
||||
if (vipMic == null) sign = 0;
|
||||
else if (vipMic.getValueObject().getGender() == (byte) 1) sign = 1;
|
||||
else if (vipMic.getValueObject().getGender() == (byte) 2) sign = 2;
|
||||
|
||||
List<Pair<Long, Integer>> positionPair = micUserIds.stream()
|
||||
.map(uid -> new Pair<>(uid, blindDateRoundConnectionService.position(roomId, uid)))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
log.info("用户与位置关系: " + JsonUtil.parseToString(positionPair));
|
||||
List<Pair<Long, Integer>> boyArea = positionPair.stream().filter(pair -> GenderArea.boyArea.contains(pair.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!boyArea.isEmpty()) {
|
||||
Pair<Long, Integer> maxPairBoy = boyArea.stream()
|
||||
.max(Comparator.comparingLong(pair -> getGiftValue(roomUid, pair.getKey())))
|
||||
.orElseThrow(() -> new ServiceException(BusiStatus.UNEXPECTEDLY_EMPTY));
|
||||
|
||||
if (sign == 1 && GenderArea.vipArea.contains(vipMic.getValueObject().getPosition().intValue())) {
|
||||
dealVipArea(roomUid, vipMic, listDTO, roomId, maxPairBoy, roomDTO, GenderEnum.MALE);
|
||||
} else {
|
||||
updateHatByArea(listDTO, maxPairBoy, roomId, roomDTO, GenderEnum.MALE);
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================================================
|
||||
List<Pair<Long, Integer>> girlArea = positionPair.stream().filter(pair -> GenderArea.girlArea.contains(pair.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
if (!girlArea.isEmpty()) {
|
||||
Pair<Long, Integer> maxPairGirl = girlArea.stream()
|
||||
.max(Comparator.comparingLong(pair -> getGiftValue(roomUid, pair.getKey())))
|
||||
.orElseThrow(() -> new ServiceException(BusiStatus.UNEXPECTEDLY_EMPTY));
|
||||
|
||||
if (sign == 2 && GenderArea.vipArea.contains(vipMic.getValueObject().getPosition().intValue())) {
|
||||
dealVipArea(roomUid, vipMic, listDTO, roomId, maxPairGirl, roomDTO, GenderEnum.FEMALE);
|
||||
} else {
|
||||
updateHatByArea(listDTO, maxPairGirl, roomId, roomDTO, GenderEnum.FEMALE);
|
||||
}
|
||||
}
|
||||
|
||||
// 主持人区
|
||||
listDTO.stream().filter(dto -> GenderArea.hostArea.contains(dto.getValueObject().getPosition().intValue()))
|
||||
.forEach(dto -> {
|
||||
QueueValueDTO queueValueDTO = dto.getValueObject();
|
||||
// 再次检查
|
||||
queueValueDTO.setVipMic(false);
|
||||
log.info("主持人区当前用户" + queueValueDTO.getUid() + "是vip?" + queueValueDTO.getVipMic());
|
||||
// 主持人区不参与抢帽子
|
||||
queueValueDTO.setCapUrl(null);
|
||||
dto.setRoomid(roomId);
|
||||
dto.setValue(JsonUtil.parseToString(queueValueDTO));
|
||||
queueService.upsert(dto);
|
||||
});
|
||||
|
||||
// vip区防止重新上麦上面的条件不满足没有处理到
|
||||
listDTO.stream().filter(dto -> GenderArea.vipArea.contains(dto.getValueObject().getPosition().intValue()))
|
||||
.forEach(dto -> {
|
||||
QueueValueDTO queueValueDTO = dto.getValueObject();
|
||||
// 再次检查
|
||||
long vipCharmVal = getGiftValue(roomUid, queueValueDTO.getUid());
|
||||
queueValueDTO.setVipMic(roomGiftValueService.checkUserIsVip(queueValueDTO.getUid(), roomDTO));
|
||||
log.info("主持人区当前用户" + queueValueDTO.getUid() + "是vip?" + queueValueDTO.getVipMic());
|
||||
if (queueValueDTO.getGender() == GenderEnum.MALE.getValue() && boyArea.isEmpty() && vipCharmVal > 0) {
|
||||
// 检查帽子
|
||||
queueValueDTO.setCapUrl(blindDateCapService.getByCharmValueAndGender(vipCharmVal, GenderEnum.MALE).getPicUrl());
|
||||
}
|
||||
if (queueValueDTO.getGender() == GenderEnum.FEMALE.getValue() && girlArea.isEmpty() && vipCharmVal > 0) {
|
||||
// 检查帽子
|
||||
queueValueDTO.setCapUrl(blindDateCapService.getByCharmValueAndGender(vipCharmVal, GenderEnum.FEMALE).getPicUrl());
|
||||
}
|
||||
dto.setRoomid(roomId);
|
||||
dto.setValue(JsonUtil.parseToString(queueValueDTO));
|
||||
queueService.upsert(dto);
|
||||
});
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (locked){
|
||||
lock.unlock();
|
||||
}
|
||||
stw.stop();
|
||||
log.info("相亲社触发器执行耗时" + stw.prettyPrint());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void dealVipArea(long roomUid, QueueDTO vipMic, List<QueueDTO> listDTO, long roomId, Pair<Long, Integer> maxPair, Room roomDTO, GenderEnum genderEnum) {
|
||||
long vipCharmVal = getGiftValue(roomUid, vipMic.getValueObject().getUid());
|
||||
long maxGiftValue = getGiftValue(roomUid, maxPair.getKey());
|
||||
List<Integer> area = null;
|
||||
if (genderEnum.equals(GenderEnum.MALE)) area = GenderArea.boyArea;
|
||||
else area = GenderArea.girlArea;
|
||||
log.info("当前vip麦位用户性别[" + genderEnum.getDesc() + "]性别所在区最大魅力值[" + maxGiftValue + "]vip用户魅力值[" + vipCharmVal + "]");
|
||||
if (vipCharmVal >= maxGiftValue) {
|
||||
// 更新队列
|
||||
List<Integer> finalArea = area;
|
||||
listDTO.stream().filter(dto -> finalArea.contains(dto.getValueObject().getPosition().intValue())).forEach(dto -> {
|
||||
QueueValueDTO queueValueDTO = dto.getValueObject();
|
||||
if (queueValueDTO.getCapUrl() != null) {
|
||||
// 再次检查
|
||||
queueValueDTO.setVipMic(roomGiftValueService.checkUserIsVip(queueValueDTO.getUid(), roomDTO));
|
||||
if (queueValueDTO.getVipMic())
|
||||
log.info("触发器1用户" + queueValueDTO.getUid() + "在麦位" + queueValueDTO.getPosition() + "获得vip");
|
||||
queueValueDTO.setCapUrl(null);
|
||||
dto.setRoomid(roomId);
|
||||
dto.setValue(JsonUtil.parseToString(queueValueDTO));
|
||||
queueService.upsert(dto);
|
||||
}
|
||||
});
|
||||
|
||||
vipMic.getValueObject().setCapUrl(blindDateCapService.getByCharmValueAndGender(vipCharmVal, genderEnum).getPicUrl());
|
||||
vipMic.setRoomid(roomId);
|
||||
vipMic.setValue(JsonUtil.parseToString(vipMic.getValueObject()));
|
||||
queueService.upsert(vipMic);
|
||||
log.info("更新 " + vipMic.getValueObject().getUid() + " 的" + genderEnum.getDesc() + "神帽子");
|
||||
} else {
|
||||
vipMic.getValueObject().setCapUrl(null);
|
||||
vipMic.getValueObject().setVipMic(roomGiftValueService.checkUserIsVip(vipMic.getValueObject().getUid(), roomDTO));
|
||||
if (!vipMic.getValueObject().getVipMic()) {
|
||||
// 移除队列
|
||||
log.info("触发器2用户" + vipMic.getValueObject().getUid() + "vip麦位position" + vipMic.getValueObject().getPosition() + "变为false");
|
||||
try {
|
||||
erBanNetEaseService.queuePoll(roomDTO.getRoomId(), vipMic.getValueObject().getPosition().toString());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
vipMic.setRoomid(roomId);
|
||||
vipMic.setValue(JsonUtil.parseToString(vipMic.getValueObject()));
|
||||
queueService.upsert(vipMic);
|
||||
log.info("更新 " + vipMic.getValueObject().getUid() + " 的" + genderEnum.getDesc() + "神帽子");
|
||||
updateHatByArea(listDTO, maxPair, roomId, roomDTO, genderEnum);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(Long roomUid) {
|
||||
log.info("清空 " + roomUid + " 的帽子");
|
||||
Room roomDTO = roomService.getRoomByUid(roomUid);
|
||||
long roomId = roomDTO.getRoomId();
|
||||
QueueListResponseDTO queueList = queryRoomService.queueList(roomId);
|
||||
queueList.getDesc().listDTO().forEach(dto -> {
|
||||
dto.getValueObject().setCapUrl(null);
|
||||
dto.setRoomid(roomId);
|
||||
dto.setValue(JsonUtil.parseToString(dto.getValueObject()));
|
||||
queueService.upsert(dto);
|
||||
});
|
||||
}
|
||||
|
||||
private long getGiftValue(long roomUserId, long userId) {
|
||||
long result = micCharmService.getByRoomUserIdAndUserId(roomUserId, userId);
|
||||
log.info(roomUserId + " 中用户 " + userId + " 的礼物魅力值为 " + result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void updateHatByArea(List<QueueDTO> listDTO, Pair<Long, Integer> maxPair, long roomId, Room roomDTO, GenderEnum genderEnum) {
|
||||
List<Integer> area = null;
|
||||
if (genderEnum.equals(GenderEnum.MALE)) area = GenderArea.boyArea;
|
||||
else area = GenderArea.girlArea;
|
||||
// 更新队列
|
||||
List<Integer> finalArea = area;
|
||||
listDTO.stream().filter(dto -> finalArea.contains(dto.getValueObject().getPosition().intValue()) && !dto.getValueObject().getPosition().equals(maxPair.getKey()))
|
||||
.forEach(dto -> {
|
||||
QueueValueDTO queueValueDTO = dto.getValueObject();
|
||||
if (queueValueDTO.getCapUrl() != null) {
|
||||
// 检查当前用户是否已经变为vip
|
||||
queueValueDTO.setVipMic(roomGiftValueService.checkUserIsVip(queueValueDTO.getUid(), roomDTO));
|
||||
if (queueValueDTO.getVipMic())
|
||||
log.info("触发器3用户" + queueValueDTO.getUid() + "在麦位" + queueValueDTO.getPosition() + "获得vip");
|
||||
queueValueDTO.setCapUrl(null);
|
||||
dto.setRoomid(roomId);
|
||||
dto.setValue(JsonUtil.parseToString(queueValueDTO));
|
||||
queueService.upsert(dto);
|
||||
}
|
||||
});
|
||||
listDTO.stream().filter(dto -> dto.getValueObject().getPosition().intValue() == maxPair.getValue()).findFirst().ifPresent(dto -> {
|
||||
QueueValueDTO queueValueDTO = dto.getValueObject();
|
||||
// 检查当前用户是否已经变为vip
|
||||
queueValueDTO.setVipMic(roomGiftValueService.checkUserIsVip(queueValueDTO.getUid(), roomDTO));
|
||||
if (queueValueDTO.getVipMic())
|
||||
log.info("触发器4用户" + queueValueDTO.getUid() + "在麦位" + queueValueDTO.getPosition() + "获得vip和帽子");
|
||||
queueValueDTO.setCapUrl(blindDateCapService.getByCharmValueAndGender(getGiftValue(roomDTO.getUid(), maxPair.getKey()), genderEnum).getPicUrl());
|
||||
dto.setRoomid(roomId);
|
||||
dto.setValue(JsonUtil.parseToString(queueValueDTO));
|
||||
queueService.upsert(dto);
|
||||
log.info("更新 " + dto.getValueObject().getUid() + " 的" + genderEnum.getDesc() + "神帽子");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -11,7 +11,7 @@ import com.accompany.common.config.SystemConfig;
|
||||
import com.accompany.common.constant.Attach;
|
||||
import com.accompany.common.constant.Constant;
|
||||
import com.accompany.common.utils.BeanUtil;
|
||||
import com.accompany.common.utils.JsonUtil;
|
||||
import com.accompany.core.util.JsonUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@@ -1,569 +1,2 @@
|
||||
package com.accompany.business.service.room.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.accompany.business.constant.GenderEnum;
|
||||
import com.accompany.business.dto.*;
|
||||
import com.accompany.business.dto.blindDate.*;
|
||||
import com.accompany.business.enums.BlindDatePhaseStateEnum;
|
||||
import com.accompany.business.enums.BlindDatePickType;
|
||||
import com.accompany.business.enums.RoomMicPositionType;
|
||||
import com.accompany.business.event.BlindDateNotifyEvent;
|
||||
import com.accompany.business.message.BlindDateNotifyMessage;
|
||||
import com.accompany.business.model.redis.RoomMic;
|
||||
import com.accompany.business.model.room.BlindDateCap;
|
||||
import com.accompany.business.model.room.BlindDateJoinHand;
|
||||
import com.accompany.business.service.room.*;
|
||||
import com.accompany.business.service.user.UsersService;
|
||||
import com.accompany.business.vo.RoomNotifyVo;
|
||||
import com.accompany.business.vo.room.BlindDataConfigVO;
|
||||
import com.accompany.common.config.SystemConfig;
|
||||
import com.accompany.common.constant.Attach;
|
||||
import com.accompany.common.constant.Constant;
|
||||
import com.accompany.common.netease.neteaseacc.result.RoomRet;
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import com.accompany.common.utils.UUIDUtil;
|
||||
import com.accompany.core.exception.ServiceException;
|
||||
import com.accompany.core.model.Room;
|
||||
import com.accompany.core.model.Users;
|
||||
import com.accompany.core.service.common.JedisService;
|
||||
import com.accompany.core.util.JsonUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.google.gson.Gson;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public class BlindDateServiceImpl implements BlindDateService {
|
||||
|
||||
private final org.slf4j.Logger log = LoggerFactory.getLogger(BlindDateServiceImpl.class);
|
||||
|
||||
@Autowired
|
||||
private BlindDateRoundConnectionService blindDateRoundConnectionService;
|
||||
|
||||
@Autowired
|
||||
private UsersService userService;
|
||||
|
||||
@Autowired
|
||||
private PushRoomService pushRoomService;
|
||||
|
||||
@Autowired
|
||||
private BlindDateRoundService blindDateRoundService;
|
||||
|
||||
@Autowired
|
||||
private QueryRoomService queryRoomService;
|
||||
|
||||
@Autowired
|
||||
private QueueService queueService;
|
||||
|
||||
@Autowired
|
||||
private BlindDateJoinHandService blindDateJoinHandService;
|
||||
|
||||
@Autowired
|
||||
private MicCharmService micCharmService;
|
||||
|
||||
@Autowired
|
||||
private BlindDateRoundHistoryService blindDateRoundHistoryService;
|
||||
|
||||
@Autowired
|
||||
private RoomService roomService;
|
||||
|
||||
@Autowired
|
||||
private BlindDateMaxGiftValueService blindDateMaxGiftValueService;
|
||||
|
||||
@Autowired
|
||||
private BlindDateRoundConnectionSuccessService blindDateRoundConnectionSuccessService;
|
||||
|
||||
@Autowired
|
||||
private RoomQueueMicroService roomQueueMicroService;
|
||||
|
||||
@Autowired
|
||||
private RoomGiftValueService roomGiftValueService;
|
||||
|
||||
@Autowired
|
||||
private JedisService jedisService;
|
||||
|
||||
@Autowired
|
||||
private IBlindDateCapService iBlindDateCapService;
|
||||
|
||||
@Autowired
|
||||
private IBlindDateJoinHandService iBlindDateJoinHandService;
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
protected Gson gson = new Gson();
|
||||
|
||||
@Override
|
||||
public List<BlindDatePickDTO> matchView(Long roundId) {
|
||||
BlindDateRoundDTO blindDateRoundDTO = blindDateRoundService.getById(roundId);
|
||||
QueueListResponseDTO queueList = queryRoomService.queueList(blindDateRoundDTO.getRoomId());
|
||||
List<QueueDTO> listDTO = queueList.getDesc().listDTO().stream()
|
||||
.filter(dto -> dto.getValueObject().getPosition() != RoomMicPositionType.TOAST_MASTE.getPosition())
|
||||
.collect(Collectors.toList());
|
||||
List<Long> onMicUserId = listDTO.stream()
|
||||
.map(dto -> dto.getValueObject().getUid())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<BlindDatePickDTO> list = blindDateRoundConnectionService.listByRoundId(roundId).stream()
|
||||
.filter(connection -> onMicUserId.contains(connection.getSelectUserId()) &&
|
||||
onMicUserId.contains(connection.getBeSelectedUserId()))
|
||||
.map(connection -> {
|
||||
BlindDatePickDTO blindDatePickDTO = new BlindDatePickDTO();
|
||||
blindDatePickDTO.setOneUserId(connection.getSelectUserId());
|
||||
blindDatePickDTO.setAnotherUserId(connection.getBeSelectedUserId());
|
||||
blindDatePickDTO.setPickType(BlindDatePickType.SINGLE);
|
||||
return blindDatePickDTO;
|
||||
})
|
||||
.collect(Collectors.toCollection(LinkedList::new));
|
||||
|
||||
List<Long> oneUserId = list.stream()
|
||||
.map(BlindDatePickDTO::getOneUserId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Long> notSelectUserId = onMicUserId.stream()
|
||||
.filter(userId -> !oneUserId.contains(userId))
|
||||
.collect(Collectors.toList());
|
||||
log.info("Users on mic but not selected or being selected: {}", JsonUtil.parseToString(notSelectUserId));
|
||||
List<BlindDatePickDTO> notSelect = notSelectUserId.stream()
|
||||
.map(userId -> {
|
||||
BlindDatePickDTO blindDatePickDTO = new BlindDatePickDTO();
|
||||
blindDatePickDTO.setOneUserId(userId);
|
||||
return blindDatePickDTO;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
list.addAll(notSelect);
|
||||
|
||||
for (BlindDatePickDTO outerBlindDatePickDTO : list) {
|
||||
for (BlindDatePickDTO innerBlindDatePickDTO : list) {
|
||||
if (outerBlindDatePickDTO != null && innerBlindDatePickDTO != null) {
|
||||
if (innerBlindDatePickDTO.getAnotherUserId() != null && outerBlindDatePickDTO.getAnotherUserId() != null && outerBlindDatePickDTO.getAnotherUserId() != null && outerBlindDatePickDTO.getOneUserId() == innerBlindDatePickDTO.getAnotherUserId() &&
|
||||
outerBlindDatePickDTO.getAnotherUserId().equals(innerBlindDatePickDTO.getOneUserId())) {
|
||||
outerBlindDatePickDTO.setPickType(BlindDatePickType.BOTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list.sort((dto1, dto2) -> {
|
||||
int pos1 = listDTO.stream()
|
||||
.filter(dto -> dto.getValueObject().getUid().equals(dto1.getOneUserId()))
|
||||
.findFirst()
|
||||
.map(dto -> dto.getValueObject().getPosition())
|
||||
.orElse(-1L).intValue();
|
||||
int pos2 = listDTO.stream()
|
||||
.filter(dto -> dto.getValueObject().getUid().equals(dto2.getOneUserId()))
|
||||
.findFirst()
|
||||
.map(dto -> dto.getValueObject().getPosition())
|
||||
.orElse(-1L).intValue();
|
||||
return Integer.compare(pos1, pos2);
|
||||
});
|
||||
|
||||
List<BlindDatePickDTO> removeBothWay = new LinkedList<>();
|
||||
LinkedList<BlindDatePickDTO> newLinkedList = new LinkedList<>(list);
|
||||
while (!newLinkedList.isEmpty()) {
|
||||
BlindDatePickDTO pop = newLinkedList.pop();
|
||||
if (removeBothWay.stream().noneMatch(dto -> pop.getAnotherUserId() != null && dto.getAnotherUserId() != null && dto.getOneUserId() == pop.getAnotherUserId() &&
|
||||
dto.getAnotherUserId().equals(pop.getOneUserId()))) {
|
||||
removeBothWay.add(pop);
|
||||
}
|
||||
}
|
||||
|
||||
LinkedList<BlindDatePickDTO> removeBothWay2 = new LinkedList<>(removeBothWay);
|
||||
List<BlindDatePickDTO> linkedList = new LinkedList<>();
|
||||
while (!removeBothWay2.isEmpty()) {
|
||||
BlindDatePickDTO pop = removeBothWay2.pop();
|
||||
if (pop.getPickType() == BlindDatePickType.BOTH) {
|
||||
BlindDatePickDTO first = new BlindDatePickDTO();
|
||||
first.setOneUserId(pop.getOneUserId());
|
||||
first.setAnotherUserId(pop.getAnotherUserId());
|
||||
first.setPickType(BlindDatePickType.SINGLE);
|
||||
|
||||
BlindDatePickDTO two = new BlindDatePickDTO();
|
||||
two.setOneUserId(pop.getAnotherUserId());
|
||||
two.setAnotherUserId(pop.getOneUserId());
|
||||
two.setPickType(BlindDatePickType.SINGLE);
|
||||
|
||||
linkedList.add(first);
|
||||
linkedList.add(two);
|
||||
}
|
||||
linkedList.add(pop);
|
||||
}
|
||||
|
||||
log.info("Matching information for round {}: {}", roundId, JsonUtil.parseToString(linkedList));
|
||||
return linkedList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DatingNotifyInfoDTO> buildNotifyDTO(Long roundId) {
|
||||
BlindDatePhaseStateEnum latestStateByRoundId = blindDateRoundHistoryService.latestStateByRoundId(roundId);
|
||||
List<BlindDatePickDTO> pick = matchView(roundId);
|
||||
BlindDateRoundDTO blindDateRoundDTO = blindDateRoundService.getById(roundId);
|
||||
List<DatingNotifyInfoDTO> list = pick.stream()
|
||||
.map(dto -> {
|
||||
Users oneUser = userService.getUsersByUid(dto.getOneUserId());
|
||||
DatingNotifyInfoDTO datingNotifyInfoDTO = new DatingNotifyInfoDTO();
|
||||
datingNotifyInfoDTO.setHasHeart(dto.getPickType() == BlindDatePickType.BOTH);
|
||||
Long another = dto.getAnotherUserId();
|
||||
Users anotherUser = userService.getUsersByUid(another);
|
||||
if (anotherUser != null) {
|
||||
datingNotifyInfoDTO.setTargetUid(anotherUser.getUid());
|
||||
datingNotifyInfoDTO.setTargetNickname(StrUtil.isNotEmpty(anotherUser.getNick()) ? anotherUser.getNick() : "");
|
||||
Integer position = blindDateRoundConnectionService.position(blindDateRoundDTO.getRoomId(), anotherUser.getUid());
|
||||
datingNotifyInfoDTO.setTargetPosition(position != null ? position : -2);
|
||||
Byte gender = anotherUser.getGender();
|
||||
datingNotifyInfoDTO.setTargetGender(gender != null ? gender : 3);
|
||||
if (datingNotifyInfoDTO.isHasHeart()) {
|
||||
String avatar = anotherUser.getAvatar();
|
||||
datingNotifyInfoDTO.setTargetAvatar(StrUtil.isNotEmpty(avatar) ? avatar : "");
|
||||
}
|
||||
}
|
||||
if (oneUser != null) {
|
||||
datingNotifyInfoDTO.setUid(oneUser.getUid());
|
||||
String oneUserNick = oneUser.getNick();
|
||||
datingNotifyInfoDTO.setNickname(StrUtil.isNotEmpty(oneUserNick) ? oneUserNick : "");
|
||||
Integer position1 = blindDateRoundConnectionService.position(blindDateRoundDTO.getRoomId(), oneUser.getUid());
|
||||
datingNotifyInfoDTO.setPosition(position1 != null ? position1 : -2);
|
||||
Byte gender1 = oneUser.getGender();
|
||||
datingNotifyInfoDTO.setGender(gender1 != null ? gender1 : 3);
|
||||
if (datingNotifyInfoDTO.isHasHeart()) {
|
||||
String avatar = oneUser.getAvatar();
|
||||
datingNotifyInfoDTO.setAvatar(StrUtil.isNotEmpty(avatar) ? avatar : "");
|
||||
}
|
||||
}
|
||||
datingNotifyInfoDTO.setHasSelectUser(dto.getPickType() != null);
|
||||
|
||||
if (dto.getPickType() == BlindDatePickType.BOTH && latestStateByRoundId == BlindDatePhaseStateEnum.PUBLISH) {
|
||||
Room roomDTO = roomService.getRoomByRoomId(blindDateRoundDTO.getRoomId());
|
||||
Long oneUserMicCharmLong = micCharmService.getByRoomUserIdAndUserId(roomDTO.getUid(), dto.getOneUserId());
|
||||
int oneUserMicCharm = 0;
|
||||
if (oneUserMicCharmLong != null) {
|
||||
oneUserMicCharm = oneUserMicCharmLong.intValue();
|
||||
}
|
||||
Long anotherUserMicCharmLong = micCharmService.getByRoomUserIdAndUserId(roomDTO.getUid(), dto.getAnotherUserId());
|
||||
int anotherUserMicCharm = 0;
|
||||
if (anotherUserMicCharmLong != null) {
|
||||
anotherUserMicCharm = anotherUserMicCharmLong.intValue();
|
||||
}
|
||||
int sum = oneUserMicCharm + anotherUserMicCharm;
|
||||
BlindDateJoinHandDTO blindDateJoinHandDTO = blindDateJoinHandService.getByCharmValue((long) sum);
|
||||
log.info("Users [{}] in room [{}] successfully matched, charm value is {}, scene is {}", dto.getOneUserId(), dto.getAnotherUserId(), sum, blindDateJoinHandDTO != null ? blindDateJoinHandDTO.getId() : null);
|
||||
if (datingNotifyInfoDTO.isHasHeart()) {
|
||||
datingNotifyInfoDTO.setSvgaUrl(blindDateJoinHandDTO != null ? blindDateJoinHandDTO.getPicUrl() : "");
|
||||
datingNotifyInfoDTO.setSvgaSecond(blindDateJoinHandDTO != null ? blindDateJoinHandDTO.getPicSecond() : null);
|
||||
}
|
||||
BlindDateRoundConnectionSuccessDTO blindDateRoundConnectionSuccessDTO = new BlindDateRoundConnectionSuccessDTO();
|
||||
blindDateRoundConnectionSuccessDTO.setBlindDateRoundId(blindDateRoundDTO.getId());
|
||||
blindDateRoundConnectionSuccessDTO.setOneUserId(dto.getOneUserId());
|
||||
blindDateRoundConnectionSuccessDTO.setOneUserGiftValue(oneUserMicCharm);
|
||||
blindDateRoundConnectionSuccessDTO.setAnotherUserId(dto.getAnotherUserId());
|
||||
blindDateRoundConnectionSuccessDTO.setAnotherUserGiftValue(anotherUserMicCharm);
|
||||
blindDateRoundConnectionSuccessDTO.setBlindDateJoinHandUrl(blindDateJoinHandDTO != null ? blindDateJoinHandDTO.getPicUrl() : "");
|
||||
blindDateRoundConnectionSuccessDTO.setSvgName(blindDateJoinHandDTO != null ? blindDateJoinHandDTO.getTitle() : "");
|
||||
blindDateRoundConnectionSuccessService.create(blindDateRoundConnectionSuccessDTO);
|
||||
if (Constant.status.valid.equals(blindDateJoinHandDTO != null ? blindDateJoinHandDTO.getNotifySwitch() : null)) {
|
||||
publishBlindDateNotifyMsg(roomDTO, datingNotifyInfoDTO.getNickname(), datingNotifyInfoDTO.getTargetNickname(), blindDateJoinHandDTO);
|
||||
}
|
||||
if (Constant.BlindRoomDefaultVal.SEND_HEADWEAR_SWITCH_OPEN.equals(blindDateJoinHandDTO != null ? blindDateJoinHandDTO.getSendHeadwearSwitch() : null)) {
|
||||
roomGiftValueService.blindDateSendHeadwear(blindDateJoinHandDTO, dto.getOneUserId(), dto.getAnotherUserId());
|
||||
}
|
||||
|
||||
datingNotifyInfoDTO = roomGiftValueService.checkAndExchange(datingNotifyInfoDTO);
|
||||
}
|
||||
return datingNotifyInfoDTO;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
log.info("Built notification information for round {}: {}", roundId, JsonUtil.parseToString(list));
|
||||
return list;
|
||||
}
|
||||
|
||||
private void publishBlindDateNotifyMsg(Room room, String oneUserNick, String anotherUserNick, BlindDateJoinHandDTO blindDateJoinHandDTO) {
|
||||
BlindDateNotifyMessage message = new BlindDateNotifyMessage();
|
||||
message.setMessId(UUIDUtil.get());
|
||||
message.setMessTime(System.currentTimeMillis());
|
||||
message.setOneUserNick(oneUserNick);
|
||||
message.setAnotherUserNick(anotherUserNick);
|
||||
message.setRoomUid(room.getUid());
|
||||
message.setRoomTitle(room.getTitle());
|
||||
message.setJoinHandLevel(blindDateJoinHandDTO != null ? blindDateJoinHandDTO.getLevel() : null);
|
||||
message.setJoinHandName(blindDateJoinHandDTO != null ? blindDateJoinHandDTO.getTitle() : null);
|
||||
message.setBackgroundUrl(blindDateJoinHandDTO != null ? blindDateJoinHandDTO.getNotifyBackgroundUrl() : null);
|
||||
message.setSweetWords(blindDateJoinHandDTO != null ? blindDateJoinHandDTO.getSweetWords() : null);
|
||||
try {
|
||||
applicationContext.publishEvent(new BlindDateNotifyEvent(message));
|
||||
} catch (Exception e) {
|
||||
log.error("publishBlindDateNotifyMsg error", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(Long roundId) {
|
||||
notifyRound(roundId);
|
||||
}
|
||||
|
||||
private void notifyRound(long roundId) {
|
||||
BlindDateRoundDTO blindDateRoundDTO = blindDateRoundService.getById(roundId);
|
||||
List<DatingNotifyInfoDTO> notifyDTOs = buildNotifyDTO(roundId);
|
||||
if (notifyDTOs.isEmpty()) {
|
||||
log.info("No one participated in this blind date, not sending messages to the client.");
|
||||
return;
|
||||
}
|
||||
Attach attach = new Attach();
|
||||
SendRoomMsgTemplate sendRoomMsgTemplate = new SendRoomMsgTemplate();
|
||||
sendRoomMsgTemplate.setFromUserId(Long.parseLong(SystemConfig.systemMessageUid));
|
||||
sendRoomMsgTemplate.setToRoomId(blindDateRoundDTO.getRoomId());
|
||||
attach.setData(new ListWrapperDTO<>(notifyDTOs));
|
||||
attach.setFirst(Constant.DefineProtocol.CUSTOM_MSG_DATING);
|
||||
attach.setSecond(Constant.DefineProtocol.CUSTOM_MSG_SUB_DATING_PUBLISH_RESULT);
|
||||
sendRoomMsgTemplate.setAttach(JsonUtil.parseToString(attach));
|
||||
pushRoomService.sendRoomMsg(sendRoomMsgTemplate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanQueue(Long roundId) {
|
||||
// 队列还原
|
||||
BlindDateRoundDTO blindDateRoundDTO = blindDateRoundService.getById(roundId);
|
||||
long roomId = blindDateRoundDTO.getRoomId();
|
||||
QueueListResponseDTO queueList = queryRoomService.queueList(roomId);
|
||||
List<QueueDTO> listDTO = queueList.getDesc().listDTO();
|
||||
for (QueueDTO dto : listDTO) {
|
||||
QueueValueDTO valueObject = dto.getValueObject();
|
||||
valueObject.setCapUrl(null);
|
||||
valueObject.setHasSelectUser(null);
|
||||
valueObject.setSelectMicPosition(null);
|
||||
dto.setRoomid(roomId);
|
||||
dto.setValue(JsonUtil.parseToString(valueObject));
|
||||
queueService.upsert(dto);
|
||||
log.info("清空" + roomId + "的相亲坑位信息");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildQueue(Long roundId) {
|
||||
List<BlindDatePickDTO> matchView = matchView(roundId);
|
||||
BlindDateRoundDTO blindDateRoundDTO = blindDateRoundService.getById(roundId);
|
||||
QueueListResponseDTO queueList = queryRoomService.queueList(blindDateRoundDTO.getRoomId());
|
||||
List<QueueDTO> listDTO = queueList.getDesc().listDTO();
|
||||
for (QueueDTO dto : listDTO) {
|
||||
QueueValueDTO valueObject = dto.getValueObject();
|
||||
BlindDatePickDTO myMatchView = matchView.stream()
|
||||
.filter(match -> match.getOneUserId() == valueObject.getUid() &&
|
||||
(match.getPickType() == BlindDatePickType.SINGLE || match.getPickType() == BlindDatePickType.BOTH))
|
||||
.findFirst().orElse(null);
|
||||
if (myMatchView != null) {
|
||||
valueObject.setSelectMicPosition(blindDateRoundConnectionService.position(blindDateRoundDTO.getRoomId(), myMatchView.getAnotherUserId()).toString());
|
||||
valueObject.setHasSelectUser(true);
|
||||
} else {
|
||||
valueObject.setSelectMicPosition(null);
|
||||
valueObject.setHasSelectUser(false);
|
||||
}
|
||||
dto.setValue(JsonUtil.parseToString(valueObject));
|
||||
dto.setRoomid(blindDateRoundDTO.getRoomId());
|
||||
queueService.upsert(dto);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disConnect(Long roundId, Long userId, Long position) {
|
||||
BlindDateRoundDTO blindDateRoundDTO = blindDateRoundService.getById(roundId);
|
||||
long roomId = blindDateRoundDTO.getRoomId();
|
||||
log.info(userId + " " + position + " 在 " + roundId + " 下麦了");
|
||||
|
||||
// Delete information related to the user's selections
|
||||
blindDateRoundConnectionService.deleteBySelectUserId(roundId, userId);
|
||||
// Delete information related to the user being selected
|
||||
blindDateRoundConnectionService.deleteByBeSelectUserId(roundId, userId);
|
||||
// Update the queue
|
||||
buildQueue(roundId);
|
||||
// Trigger the calculation of the highest hat value
|
||||
blindDateMaxGiftValueService.trigger(roomService.getRoomByRoomId(roomId).getUid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBlindDateInfo(Long roomUid, boolean isOpen) {
|
||||
Room roomByUserId = roomService.getRoomByUid(roomUid);
|
||||
int posStatus = 1;
|
||||
BlindDateRoundDTO latestByRoomId = null;
|
||||
if (isOpen) {
|
||||
Integer[] modeTypeList = {Constant.RoomModeType.NORMAL_MODE, Constant.RoomModeType.CLOSE_PK_MODE, Constant.RoomModeType.OPEN_MICRO_MODE, Constant.RoomModeType.CLOSE_MICRO_MODE};
|
||||
if (!Arrays.asList(modeTypeList).contains(roomByUserId.getRoomModeType())) {
|
||||
throw new ServiceException(BusiStatus.THE_CURRENT_ROOM_MODE_CANNOT_ENABLE_BLIND_DATES);
|
||||
}
|
||||
|
||||
// 创建新的一轮相亲
|
||||
BlindDateRoundDTO blindDateRoundDTO = new BlindDateRoundDTO();
|
||||
blindDateRoundDTO.setRoomId(roomByUserId.getRoomId());
|
||||
blindDateRoundService.create(blindDateRoundDTO);
|
||||
latestByRoomId = blindDateRoundService.latestByRoomId(roomByUserId.getRoomId());
|
||||
BlindDatePhaseStateEnum currentRoomState = blindDateRoundService.currentRoomState(roomByUserId.getRoomId());
|
||||
// 新增阶段轮次记录
|
||||
BlindDateRoundHistoryDTO blindDateRoundHistoryDTO = new BlindDateRoundHistoryDTO();
|
||||
blindDateRoundHistoryDTO.setBlindDateRoundId(latestByRoomId.getId());
|
||||
blindDateRoundHistoryDTO.setState(currentRoomState);
|
||||
blindDateRoundHistoryDTO.setRoomId(roomByUserId.getRoomId());
|
||||
blindDateRoundHistoryService.create(blindDateRoundHistoryDTO);
|
||||
roomByUserId.setBlindDateState(currentRoomState.getCode());
|
||||
roomByUserId.setRoomModeType(Constant.RoomModeType.OPEN_BLIND_DATE_MODE);
|
||||
// 打开礼物魅力值
|
||||
if (!roomByUserId.getShowGiftValue()) {
|
||||
roomByUserId.setShowGiftValue(true);
|
||||
}
|
||||
// 发房间公屏
|
||||
if (BlindDatePhaseStateEnum.COMMUNICATING.getTip() != null) {
|
||||
SendRoomMsgTemplate sendRoomMsgTemplate = new SendRoomMsgTemplate();
|
||||
sendRoomMsgTemplate.setFromUserId(Long.parseLong(SystemConfig.systemMessageUid));
|
||||
sendRoomMsgTemplate.setToRoomId(roomByUserId.getRoomId());
|
||||
sendRoomMsgTemplate.setAttach(BlindDatePhaseStateEnum.COMMUNICATING.getTip());
|
||||
pushRoomService.sendTip(sendRoomMsgTemplate);
|
||||
}
|
||||
// 清空魅力值
|
||||
try {
|
||||
roomGiftValueService.cleanGiftValueCache(roomUid);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
latestByRoomId = blindDateRoundService.latestByRoomId(roomByUserId.getRoomId());
|
||||
roomByUserId.setBlindDateState(BlindDatePhaseStateEnum.CLOSED.getCode());
|
||||
roomByUserId.setRoomModeType(Constant.RoomModeType.NORMAL_MODE);
|
||||
}
|
||||
// 更新数据库房间信息
|
||||
roomService.updateDbAndCacheRoomInfo(roomByUserId);
|
||||
if (roomByUserId.getBlindDateVipUid() != 0L) {
|
||||
roomByUserId.setBlindDateVipUid(0L);
|
||||
roomService.updateBlindDateVipUid(0L, roomByUserId);
|
||||
|
||||
Room endRoom = roomService.getRoomByUid(roomUid);
|
||||
RoomNotifyVo roomNotifyVo = new RoomNotifyVo();
|
||||
roomNotifyVo.setRoomInfo(gson.toJson(endRoom));
|
||||
roomNotifyVo.setType(Constant.RoomMic.ROOM_NOTIFY_TYPE_ROOM);
|
||||
try {
|
||||
roomService.updateNetEaseRoomInfo(endRoom, gson.toJson(roomNotifyVo));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isOpen) {
|
||||
roomGiftValueService.cleanRoomRoundSendGiftVal(roomByUserId.getUid(), latestByRoomId != null ? latestByRoomId.getId() : null);
|
||||
}
|
||||
//更新麦序信息到缓存
|
||||
List<RoomMic> micInfo = roomQueueMicroService.queueBatchUpdate(roomByUserId, posStatus);
|
||||
RoomNotifyVo roomNotifyVo = new RoomNotifyVo();
|
||||
roomNotifyVo.setRoomInfo(gson.toJson(roomByUserId));
|
||||
roomNotifyVo.setMicInfo(gson.toJson(micInfo));
|
||||
roomNotifyVo.setType(Constant.RoomMic.ROOM_NOTIFY_TYPE_ROOM_MIC);
|
||||
//通知云信更新聊天室信息
|
||||
RoomRet roomRet = null;
|
||||
try {
|
||||
roomRet = roomService.updateNetEaseRoomInfo(roomByUserId, JSON.toJSONString(roomNotifyVo));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (roomRet.getCode() == 200) {
|
||||
// 清除队列中有关相亲的信息
|
||||
if (latestByRoomId != null) {
|
||||
cleanQueue(latestByRoomId.getId());
|
||||
}
|
||||
roomGiftValueService.pushCurrentGiftValues2Client(roomUid);
|
||||
// 清除房间排麦队列
|
||||
jedisService.hdel(RedisKey.room_mic_queue.getKey(), Long.toString(roomUid));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlindDataConfigVO getBlindDataConfig() {
|
||||
BlindDataConfigVO blindDataConfigVO = new BlindDataConfigVO();
|
||||
List<BlindDateCap> capList = iBlindDateCapService.list();
|
||||
List<BlindDateCap> femaleCapList = new ArrayList<>();
|
||||
List<BlindDateCap> maleCapList = new ArrayList<>();
|
||||
if (capList != null && !capList.isEmpty()) {
|
||||
for (BlindDateCap cap : capList) {
|
||||
if (cap.getGender() == GenderEnum.MALE) {
|
||||
maleCapList.add(cap);
|
||||
} else {
|
||||
femaleCapList.add(cap);
|
||||
}
|
||||
}
|
||||
}
|
||||
List<BlindDateJoinHand> joinHandList = iBlindDateJoinHandService.listBlindDateJoinHandWithOutDefault();
|
||||
blindDataConfigVO.setFemaleCapList(femaleCapList);
|
||||
blindDataConfigVO.setMaleCapList(maleCapList);
|
||||
blindDataConfigVO.setJoinHandList(joinHandList);
|
||||
return blindDataConfigVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testAA() {
|
||||
List<BlindDatePickDTO> list = new ArrayList<>();
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
BlindDatePickDTO blindDatePickDTO = new BlindDatePickDTO();
|
||||
blindDatePickDTO.setOneUserId((long) i);
|
||||
if (!(i == 5 || i == 6)) blindDatePickDTO.setAnotherUserId((long) (i + 10));
|
||||
// 默认单选
|
||||
blindDatePickDTO.setPickType(BlindDatePickType.SINGLE);
|
||||
list.add(blindDatePickDTO);
|
||||
}
|
||||
|
||||
for (int i = 11; i <= 20; i++) {
|
||||
BlindDatePickDTO blindDatePickDTO = new BlindDatePickDTO();
|
||||
blindDatePickDTO.setOneUserId((long) i);
|
||||
blindDatePickDTO.setAnotherUserId((long) (i - 10));
|
||||
// 默认单选
|
||||
blindDatePickDTO.setPickType(BlindDatePickType.SINGLE);
|
||||
list.add(blindDatePickDTO);
|
||||
}
|
||||
|
||||
// 2.查看是否有双选
|
||||
for (BlindDatePickDTO outerBlindDatePickDTO : list) {
|
||||
for (BlindDatePickDTO innerBlindDatePickDTO : list) {
|
||||
if (innerBlindDatePickDTO.getAnotherUserId() != null && outerBlindDatePickDTO.getAnotherUserId() != null && Objects.equals(outerBlindDatePickDTO.getOneUserId(), innerBlindDatePickDTO.getAnotherUserId()) &&
|
||||
Objects.equals(outerBlindDatePickDTO.getAnotherUserId(), innerBlindDatePickDTO.getOneUserId())) {
|
||||
outerBlindDatePickDTO.setPickType(BlindDatePickType.BOTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4.去除双向选择
|
||||
LinkedList<BlindDatePickDTO> newLinkedList = new LinkedList<>(list);
|
||||
List<BlindDatePickDTO> removeBothWay = new ArrayList<>();
|
||||
while (!newLinkedList.isEmpty()) {
|
||||
BlindDatePickDTO pop = newLinkedList.pop();
|
||||
if (removeBothWay.stream().noneMatch(it -> it.getAnotherUserId() != null && pop.getAnotherUserId() != null && Objects.equals(it.getOneUserId(), pop.getAnotherUserId()) && Objects.equals(it.getAnotherUserId(), pop.getOneUserId()))) {
|
||||
removeBothWay.add(pop);
|
||||
}
|
||||
}
|
||||
|
||||
//5.添加多余信息
|
||||
LinkedList<BlindDatePickDTO> removeBothWay2 = new LinkedList<>(removeBothWay);
|
||||
List<BlindDatePickDTO> linkedList = new ArrayList<>();
|
||||
while (!removeBothWay2.isEmpty()) {
|
||||
BlindDatePickDTO pop = removeBothWay2.pop();
|
||||
if (pop.getPickType() == BlindDatePickType.BOTH) {
|
||||
// 添加两条互相选择的实体
|
||||
BlindDatePickDTO first = new BlindDatePickDTO();
|
||||
first.setOneUserId(pop.getOneUserId());
|
||||
first.setAnotherUserId(pop.getAnotherUserId());
|
||||
first.setPickType(BlindDatePickType.SINGLE);
|
||||
|
||||
BlindDatePickDTO two = new BlindDatePickDTO();
|
||||
two.setOneUserId(pop.getAnotherUserId());
|
||||
two.setAnotherUserId(pop.getOneUserId());
|
||||
two.setPickType(BlindDatePickType.SINGLE);
|
||||
|
||||
linkedList.add(first);
|
||||
linkedList.add(two);
|
||||
}
|
||||
linkedList.add(pop);
|
||||
}
|
||||
|
||||
log.info("配对信息" + JsonUtil.parseToString(linkedList));
|
||||
}
|
||||
}
|
||||
|
@@ -30,11 +30,6 @@ public class WebMVCConfig implements WebMvcConfigurer {
|
||||
return new ModelHallAuthInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebInterceptor getWebInterceptor() {
|
||||
return new WebInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public XssInterceptor getXssInterceptor() {
|
||||
return new XssInterceptor();
|
||||
@@ -55,7 +50,6 @@ public class WebMVCConfig implements WebMvcConfigurer {
|
||||
registry.addInterceptor(getLoginInterceptor()).addPathPatterns("/**");
|
||||
//registry.addInterceptor(getAppVersionInterceptor());
|
||||
registry.addInterceptor(getModelHallAuthInterceptor());
|
||||
registry.addInterceptor(getWebInterceptor());
|
||||
registry.addInterceptor(getXssInterceptor());
|
||||
}
|
||||
|
||||
|
@@ -4,7 +4,6 @@ import com.accompany.business.common.BaseController;
|
||||
import com.accompany.business.service.exchange.GoldExchangeDiamondService;
|
||||
import com.accompany.business.vo.exchange.GoldExchangeDiamondVo;
|
||||
import com.accompany.common.annotation.Authorization;
|
||||
import com.accompany.common.annotation.H5Authorization;
|
||||
import com.accompany.common.result.BusiResult;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import com.accompany.core.exception.ServiceException;
|
||||
@@ -29,7 +28,6 @@ public class GoldExchangeDiamondController extends BaseController {
|
||||
private GoldExchangeDiamondService service;
|
||||
|
||||
@Authorization
|
||||
@H5Authorization
|
||||
@ApiOperation("获取配置")
|
||||
@GetMapping(value = "/getConfig")
|
||||
public BusiResult<GoldExchangeDiamondVo> getConfig(HttpServletRequest request) {
|
||||
@@ -39,7 +37,6 @@ public class GoldExchangeDiamondController extends BaseController {
|
||||
}
|
||||
|
||||
@Authorization
|
||||
@H5Authorization
|
||||
@ApiOperation("兑换")
|
||||
@PostMapping(value = "/exchange")
|
||||
public BusiResult<Void> exchange(HttpServletRequest request, Long goldNum, Long diamondNum) {
|
||||
|
@@ -6,7 +6,6 @@ import com.accompany.core.enumeration.BillObjTypeEnum;
|
||||
import com.accompany.core.enumeration.BillTypeEnum;
|
||||
import com.accompany.sharding.vo.BillRecordDateVo;
|
||||
import com.accompany.common.annotation.Authorization;
|
||||
import com.accompany.common.annotation.H5Authorization;
|
||||
import com.accompany.common.result.BusiResult;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import com.accompany.core.enumeration.CurrencyEnum;
|
||||
|
@@ -6,7 +6,6 @@ import com.accompany.business.service.room.RoomRevenueService;
|
||||
import com.accompany.business.service.room.RoomService;
|
||||
import com.accompany.business.service.user.UsersService;
|
||||
import com.accompany.common.annotation.Authorization;
|
||||
import com.accompany.common.annotation.H5Authorization;
|
||||
import com.accompany.common.constant.Constant;
|
||||
import com.accompany.common.result.BusiResult;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
@@ -44,7 +43,6 @@ public class RoomRevenueController extends BaseController {
|
||||
@ApiOperation("获取本周牌照房流水")
|
||||
@GetMapping(value = "/weekTotal")
|
||||
@Authorization
|
||||
@H5Authorization
|
||||
public BusiResult weekTotalRevenue() {
|
||||
Long uid = getUid();
|
||||
logger.info("weekTotalRevenue, uid:{}", uid);
|
||||
@@ -54,7 +52,6 @@ public class RoomRevenueController extends BaseController {
|
||||
@ApiOperation("生成本周牌照房流水excel下载链接")
|
||||
@GetMapping(value = "/exportExcel")
|
||||
@Authorization
|
||||
@H5Authorization
|
||||
public BusiResult exportExcel(@RequestParam("start") String start, @RequestParam("end") String end, @RequestParam(value="erbanNo", required = false) Long erbanNo) throws Exception {
|
||||
Long uid = getUid();
|
||||
if (erbanNo != null) {
|
||||
@@ -80,7 +77,6 @@ public class RoomRevenueController extends BaseController {
|
||||
@ApiOperation("获取本周个播房流水")
|
||||
@GetMapping(value = "/singleBroadcast/weekTotal")
|
||||
@Authorization
|
||||
@H5Authorization
|
||||
public BusiResult SingleBroadcastweekTotalRevenue() {
|
||||
Long uid = getUid();
|
||||
logger.info("singleBroadcast weekTotalRevenue, uid:{}", uid);
|
||||
@@ -90,7 +86,6 @@ public class RoomRevenueController extends BaseController {
|
||||
@ApiOperation("生成本周个播房流水excel下载链接")
|
||||
@GetMapping(value = "/singleroom/exportExcel")
|
||||
@Authorization
|
||||
@H5Authorization
|
||||
public BusiResult singleroomExportExcel(@RequestParam("start") String start, @RequestParam("end") String end) throws Exception {
|
||||
Long uid = getUid();
|
||||
logger.info("singleroomExportExcel, uid:{}, start:{}, end:{}", uid, start, end);
|
||||
|
@@ -5,7 +5,6 @@ import com.accompany.business.service.purse.DiamondGiveHistoryService;
|
||||
import com.accompany.business.vo.DiamondGiveHistoryPageVo;
|
||||
import com.accompany.business.vo.SimpleUserVo;
|
||||
import com.accompany.common.annotation.Authorization;
|
||||
import com.accompany.common.annotation.H5Authorization;
|
||||
import com.accompany.common.result.BusiResult;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import com.accompany.core.exception.ServiceException;
|
||||
@@ -79,7 +78,6 @@ public class DiamondGiveHistoryController extends BaseController {
|
||||
|
||||
@ApiOperation(value = "转赠钻石/礼物 -> 用户搜索", httpMethod = "POST")
|
||||
@Authorization
|
||||
@H5Authorization
|
||||
@PostMapping("/searchUser")
|
||||
public BusiResult<SimpleUserVo> searchUser(Long erbanNo) {
|
||||
Long uid = this.getUid();
|
||||
|
@@ -5,7 +5,6 @@ import com.accompany.business.service.purse.UserPurseService;
|
||||
import com.accompany.business.vo.UserPurseVo;
|
||||
import com.accompany.business.vo.UserPurseWithRoomTypeVo;
|
||||
import com.accompany.common.annotation.Authorization;
|
||||
import com.accompany.common.annotation.H5Authorization;
|
||||
import com.accompany.common.result.BusiResult;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -24,7 +23,6 @@ public class UserPurseController extends BaseController {
|
||||
private UserPurseService userPurseService;
|
||||
|
||||
@GetMapping({"query", "query2"})
|
||||
@H5Authorization
|
||||
@Authorization
|
||||
public BusiResult<UserPurseVo> queryMyUserPurse(HttpServletRequest request){
|
||||
Long uid = getUid(request);
|
||||
@@ -36,7 +34,6 @@ public class UserPurseController extends BaseController {
|
||||
}
|
||||
|
||||
@GetMapping("queryWithRoomType")
|
||||
@H5Authorization
|
||||
@Authorization
|
||||
public BusiResult<UserPurseWithRoomTypeVo> queryMyUserPurseWithRoomType(HttpServletRequest request){
|
||||
Long uid = getUid(request);
|
||||
|
@@ -69,19 +69,6 @@ public abstract class BasicInterceptor implements HandlerInterceptor {
|
||||
return ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取@H5Authorization注解需要的h5_token
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public String getH5Token(HttpServletRequest request) {
|
||||
String token = request.getHeader(ApplicationConstant.PublicParameters.H5_TOKEN);
|
||||
if (StringUtils.isBlank(token)) {
|
||||
token = request.getParameter(ApplicationConstant.PublicParameters.H5_TOKEN);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回重新登录提示内容
|
||||
|
@@ -3,7 +3,6 @@ package com.accompany.business.interceptor;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.accompany.common.annotation.Authorization;
|
||||
import com.accompany.common.annotation.H5Authorization;
|
||||
import com.accompany.common.constant.ApplicationConstant;
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
import com.accompany.common.utils.EnvComponent;
|
||||
@@ -60,13 +59,6 @@ public class LoginInterceptor extends BasicInterceptor {
|
||||
if (method.getAnnotation(Authorization.class) == null) {
|
||||
return true;
|
||||
}
|
||||
// 如果同时有H5Authorization注解并且h5_token不为空使用H5Authorization校验
|
||||
if (method.getAnnotation(H5Authorization.class) != null && StringUtils.isNotBlank(getH5Token(request))) {
|
||||
// 如果请求头部信息同时有ticket和token,则会使用Authorization注解校验用户登录信息
|
||||
if (StringUtils.isBlank(getTicket(request))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
String uid = this.getUid(request);
|
||||
if (StringUtils.isEmpty(uid) || StringUtils.equalsIgnoreCase(uid, "null") || !StringUtils.isNumeric(uid)) {
|
||||
logger.warn("uid illegal, uri={}, uid={}", request.getRequestURI(), uid);
|
||||
@@ -121,8 +113,4 @@ public class LoginInterceptor extends BasicInterceptor {
|
||||
return ticketStr;
|
||||
}
|
||||
|
||||
private String getH5JwtToken(HttpServletRequest request) {
|
||||
return request.getParameter(ApplicationConstant.PublicParameters.H5_TOKEN);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,105 +0,0 @@
|
||||
package com.accompany.business.interceptor;
|
||||
|
||||
import com.accompany.common.annotation.Authorization;
|
||||
import com.accompany.common.annotation.H5Authorization;
|
||||
import com.accompany.common.constant.ApplicationConstant;
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
import com.accompany.common.utils.StringUtils;
|
||||
import com.accompany.core.service.common.JedisService;
|
||||
import com.accompany.core.util.JwtUtils;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Created by yuanyi on 2019/3/26.
|
||||
*/
|
||||
public class WebInterceptor extends BasicInterceptor {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
|
||||
|
||||
@Autowired
|
||||
private JedisService jedisService;
|
||||
|
||||
@Autowired
|
||||
private JwtUtils jwtUtils;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
if (!(handler instanceof HandlerMethod)) {
|
||||
return true;
|
||||
}
|
||||
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||
Method method = handlerMethod.getMethod();
|
||||
// 不需要登录校验
|
||||
if (method.getAnnotation(H5Authorization.class) == null) {
|
||||
return true;
|
||||
}
|
||||
// 如果同时有Authorization注解并且ticket不为空使用Authorization校验
|
||||
if (method.getAnnotation(Authorization.class) != null && StringUtils.isNotBlank(getTicket(request))) {
|
||||
return true;
|
||||
}
|
||||
String uid = this.getUid(request);
|
||||
if (StringUtils.isEmpty(uid) || StringUtils.equalsIgnoreCase(uid, "null") || !StringUtils.isNumeric(uid)) {
|
||||
logger.warn("uid illegal, uri={}, uid={}", request.getRequestURI(), uid);
|
||||
writeLoginExpireResponse(response, 401, "Login status has expired, please log in again ~");
|
||||
return false;
|
||||
}
|
||||
// h5登录校验
|
||||
if (method.getAnnotation(H5Authorization.class) != null) {
|
||||
String token = getH5Token(request);
|
||||
if (StringUtils.isEmpty(token) || StringUtils.equalsIgnoreCase(token, "null")) {
|
||||
logger.warn("jwtToken is null, uri={}, uid={}", request.getRequestURI(), uid);
|
||||
writeLoginExpireResponse(response, 401, "Login status has expired, please log in again ~");
|
||||
return false;
|
||||
}
|
||||
String realToken = this.jedisService.hget(RedisKey.h5loginjwtoken.getKey(), uid);
|
||||
if (StringUtils.isEmpty(realToken)) {
|
||||
logger.warn("jwtToken is not exists, uri={}, uid={}, token={}", request.getRequestURI(), uid, token);
|
||||
writeLoginExpireResponse(response, 401, "Login status has expired, please log in again ~");
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
jwtUtils.parseJWT(token);
|
||||
} catch (ExpiredJwtException e) {
|
||||
logger.error("jwtToken is expired,uid={},token={}", uid, token);
|
||||
writeLoginExpireResponse(response, 406, "need login!");
|
||||
return false;
|
||||
} catch (SignatureException e) {
|
||||
logger.error("signature is illegal,uid={},token={}", uid, token);
|
||||
writeLoginExpireResponse(response, 407, "Login status has expired, please log in again ~");
|
||||
return false;
|
||||
}
|
||||
if (!realToken.equals(token)) {
|
||||
logger.warn("jwtToken illegal, uri={}, uid={}, token={}", request.getRequestURI(), uid, token);
|
||||
writeLoginExpireResponse(response, 401, "Login status has expired, please log in again ~");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 uid, 以业务参数为首选
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
private String getUid(HttpServletRequest request) {
|
||||
String uidStr = request.getParameter(ApplicationConstant.PublicParameters.UID);
|
||||
if (StringUtils.isEmpty(uidStr)) {
|
||||
uidStr = request.getHeader(ApplicationConstant.PublicParameters.PUB_UID);
|
||||
}
|
||||
return uidStr;
|
||||
}
|
||||
|
||||
}
|
@@ -45,7 +45,6 @@
|
||||
<hibernate-validator.version>6.0.17.Final</hibernate-validator.version>
|
||||
<hanlp.version>portable-1.6.8</hanlp.version>
|
||||
<hanlp-lucene-plugin.version>1.1.6</hanlp-lucene-plugin.version>
|
||||
<jjwt.version>0.9.1</jjwt.version>
|
||||
<java-jwt.version>3.10.3</java-jwt.version>
|
||||
<sitemesh.version>2.4.2</sitemesh.version>
|
||||
<pagehelper.version>6.1.0</pagehelper.version>
|
||||
@@ -78,6 +77,7 @@
|
||||
<pinyin4j.version>2.5.1</pinyin4j.version>
|
||||
<aws.version>2.30.37</aws.version>
|
||||
<dynamic-tp.version>1.2.2</dynamic-tp.version>
|
||||
<jjwt.version>0.12.5</jjwt.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
@@ -6,7 +6,7 @@ package com.accompany.oauth.constant;
|
||||
* @author Accompany OAuth Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public enum AuthType {
|
||||
public enum GrantTypeEnum {
|
||||
/**
|
||||
* 密码登录
|
||||
*/
|
||||
@@ -35,7 +35,7 @@ public enum AuthType {
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
AuthType(String code, String description) {
|
||||
GrantTypeEnum(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
@@ -48,8 +48,8 @@ public enum AuthType {
|
||||
return description;
|
||||
}
|
||||
|
||||
public static AuthType fromCode(String code) {
|
||||
for (AuthType type : values()) {
|
||||
public static GrantTypeEnum fromCode(String code) {
|
||||
for (GrantTypeEnum type : values()) {
|
||||
if (type.code.equals(code)) {
|
||||
return type;
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
package com.accompany.oauth.dto;
|
||||
|
||||
import com.accompany.oauth.constant.AuthType;
|
||||
import com.accompany.oauth.constant.GrantTypeEnum;
|
||||
import com.accompany.common.device.DeviceInfo;
|
||||
|
||||
/**
|
||||
* 认证凭据DTO
|
||||
@@ -13,7 +14,7 @@ public class AuthCredentials {
|
||||
/**
|
||||
* 认证类型
|
||||
*/
|
||||
private AuthType type;
|
||||
private GrantTypeEnum type;
|
||||
|
||||
/**
|
||||
* 主体标识(手机号/邮箱/OpenID等)
|
||||
@@ -43,17 +44,17 @@ public class AuthCredentials {
|
||||
public AuthCredentials() {
|
||||
}
|
||||
|
||||
public AuthCredentials(AuthType type, String principal, String credentials) {
|
||||
public AuthCredentials(GrantTypeEnum type, String principal, String credentials) {
|
||||
this.type = type;
|
||||
this.principal = principal;
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
public AuthType getType() {
|
||||
public GrantTypeEnum getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(AuthType type) {
|
||||
public void setType(GrantTypeEnum type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,5 @@
|
||||
package com.accompany.oauth.dto;
|
||||
|
||||
import com.accompany.oauth.model.UserDetails;
|
||||
|
||||
/**
|
||||
* 认证结果DTO
|
||||
*
|
||||
@@ -50,6 +48,21 @@ public class AuthResult {
|
||||
*/
|
||||
private String accid = "";
|
||||
|
||||
/**
|
||||
* 用户Token (兼容oauth2)
|
||||
*/
|
||||
private String userToken = "";
|
||||
|
||||
/**
|
||||
* 登录Key (兼容oauth2)
|
||||
*/
|
||||
private String loginKey = "";
|
||||
|
||||
/**
|
||||
* JWT ID (兼容oauth2)
|
||||
*/
|
||||
private String jti = "";
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
@@ -129,6 +142,30 @@ public class AuthResult {
|
||||
this.accid = accid;
|
||||
}
|
||||
|
||||
public String getUserToken() {
|
||||
return userToken;
|
||||
}
|
||||
|
||||
public void setUserToken(String userToken) {
|
||||
this.userToken = userToken;
|
||||
}
|
||||
|
||||
public String getLoginKey() {
|
||||
return loginKey;
|
||||
}
|
||||
|
||||
public void setLoginKey(String loginKey) {
|
||||
this.loginKey = loginKey;
|
||||
}
|
||||
|
||||
public String getJti() {
|
||||
return jti;
|
||||
}
|
||||
|
||||
public void setJti(String jti) {
|
||||
this.jti = jti;
|
||||
}
|
||||
|
||||
public UserInfo getUserInfo() {
|
||||
return userInfo;
|
||||
}
|
||||
@@ -148,6 +185,9 @@ public class AuthResult {
|
||||
", uid=" + uid +
|
||||
", netEaseToken='" + netEaseToken + '\'' +
|
||||
", accid='" + accid + '\'' +
|
||||
", userToken='" + userToken + '\'' +
|
||||
", loginKey='" + loginKey + '\'' +
|
||||
", jti='" + jti + '\'' +
|
||||
", userInfo=" + userInfo +
|
||||
'}';
|
||||
}
|
||||
|
@@ -1,122 +0,0 @@
|
||||
package com.accompany.oauth.dto;
|
||||
|
||||
/**
|
||||
* 设备信息DTO
|
||||
*
|
||||
* @author Accompany OAuth Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class DeviceInfo {
|
||||
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 设备类型(iOS/Android/Web等)
|
||||
*/
|
||||
private String deviceType;
|
||||
|
||||
/**
|
||||
* 设备型号
|
||||
*/
|
||||
private String deviceModel;
|
||||
|
||||
/**
|
||||
* 操作系统版本
|
||||
*/
|
||||
private String osVersion;
|
||||
|
||||
/**
|
||||
* 应用版本
|
||||
*/
|
||||
private String appVersion;
|
||||
|
||||
/**
|
||||
* IP地址
|
||||
*/
|
||||
private String ipAddress;
|
||||
|
||||
/**
|
||||
* User-Agent
|
||||
*/
|
||||
private String userAgent;
|
||||
|
||||
public DeviceInfo() {
|
||||
}
|
||||
|
||||
public DeviceInfo(String deviceId, String deviceType) {
|
||||
this.deviceId = deviceId;
|
||||
this.deviceType = deviceType;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getDeviceType() {
|
||||
return deviceType;
|
||||
}
|
||||
|
||||
public void setDeviceType(String deviceType) {
|
||||
this.deviceType = deviceType;
|
||||
}
|
||||
|
||||
public String getDeviceModel() {
|
||||
return deviceModel;
|
||||
}
|
||||
|
||||
public void setDeviceModel(String deviceModel) {
|
||||
this.deviceModel = deviceModel;
|
||||
}
|
||||
|
||||
public String getOsVersion() {
|
||||
return osVersion;
|
||||
}
|
||||
|
||||
public void setOsVersion(String osVersion) {
|
||||
this.osVersion = osVersion;
|
||||
}
|
||||
|
||||
public String getAppVersion() {
|
||||
return appVersion;
|
||||
}
|
||||
|
||||
public void setAppVersion(String appVersion) {
|
||||
this.appVersion = appVersion;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public String getUserAgent() {
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
public void setUserAgent(String userAgent) {
|
||||
this.userAgent = userAgent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DeviceInfo{" +
|
||||
"deviceId='" + deviceId + '\'' +
|
||||
", deviceType='" + deviceType + '\'' +
|
||||
", deviceModel='" + deviceModel + '\'' +
|
||||
", osVersion='" + osVersion + '\'' +
|
||||
", appVersion='" + appVersion + '\'' +
|
||||
", ipAddress='" + ipAddress + '\'' +
|
||||
", userAgent='" + userAgent + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@@ -1,11 +1,14 @@
|
||||
package com.accompany.oauth.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* OAuth Token请求DTO
|
||||
*
|
||||
* @author Accompany OAuth Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Data
|
||||
public class TokenRequest {
|
||||
|
||||
/**
|
||||
@@ -23,6 +26,11 @@ public class TokenRequest {
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 验证码 (兼容OAuth2)
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 客户端ID
|
||||
*/
|
||||
@@ -33,11 +41,6 @@ public class TokenRequest {
|
||||
*/
|
||||
private String clientSecret;
|
||||
|
||||
/**
|
||||
* 权限范围
|
||||
*/
|
||||
private String scope;
|
||||
|
||||
/**
|
||||
* 刷新令牌(当grant_type为refresh_token时使用)
|
||||
*/
|
||||
@@ -52,94 +55,5 @@ public class TokenRequest {
|
||||
* 设备ID
|
||||
*/
|
||||
private String deviceId;
|
||||
|
||||
public TokenRequest() {
|
||||
}
|
||||
|
||||
public String getGrantType() {
|
||||
return grantType;
|
||||
}
|
||||
|
||||
public void setGrantType(String grantType) {
|
||||
this.grantType = grantType;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
public void setClientSecret(String clientSecret) {
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public void setScope(String scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public String getRefreshToken() {
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
public void setRefreshToken(String refreshToken) {
|
||||
this.refreshToken = refreshToken;
|
||||
}
|
||||
|
||||
public String getThirdType() {
|
||||
return thirdType;
|
||||
}
|
||||
|
||||
public void setThirdType(String thirdType) {
|
||||
this.thirdType = thirdType;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TokenRequest{" +
|
||||
"grantType='" + grantType + '\'' +
|
||||
", username='" + username + '\'' +
|
||||
", password='[PROTECTED]'" +
|
||||
", clientId='" + clientId + '\'' +
|
||||
", clientSecret='[PROTECTED]'" +
|
||||
", scope='" + scope + '\'' +
|
||||
", refreshToken='[PROTECTED]'" +
|
||||
", thirdType='" + thirdType + '\'' +
|
||||
", deviceId='" + deviceId + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
package com.accompany.oauth.ticket;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Ticket接口 - OAuth访问票据
|
||||
* 迁移自OAuth2模块的Ticket接口
|
||||
*
|
||||
* @author Accompany OAuth Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface Ticket {
|
||||
|
||||
String ONCE_TYPE = "once";
|
||||
|
||||
String MULTI_TYPE = "multi";
|
||||
|
||||
/**
|
||||
* 获取附加信息
|
||||
*
|
||||
* @return 附加信息Map
|
||||
*/
|
||||
Map<String, Object> getAdditionalInformation();
|
||||
|
||||
/**
|
||||
* 获取关联的访问令牌
|
||||
*
|
||||
* @return 访问令牌值
|
||||
*/
|
||||
String getAccessToken();
|
||||
|
||||
/**
|
||||
* 获取票据类型
|
||||
*
|
||||
* @return 票据类型
|
||||
*/
|
||||
String getTicketType();
|
||||
|
||||
/**
|
||||
* 获取过期时间
|
||||
*
|
||||
* @return 过期时间
|
||||
*/
|
||||
Date getExpiration();
|
||||
|
||||
/**
|
||||
* 获取过期时间(秒)
|
||||
*
|
||||
* @return 过期秒数
|
||||
*/
|
||||
int getExpiresIn();
|
||||
|
||||
/**
|
||||
* 获取票据值
|
||||
*
|
||||
* @return 票据值
|
||||
*/
|
||||
String getValue();
|
||||
|
||||
/**
|
||||
* 获取权限范围
|
||||
*
|
||||
* @return 权限范围Set
|
||||
*/
|
||||
Set<String> getScope();
|
||||
}
|
@@ -55,6 +55,31 @@
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<!-- 移除Spring Security相关依赖,使用轻量化实现 -->
|
||||
|
||||
<!-- JWT 支持:API -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>${jjwt.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JWT 支持:实现 -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>${jjwt.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- JWT 支持:JSON 序列化/反序列化(选一个)-->
|
||||
<!-- 如果你项目用 Jackson(Spring Boot 默认),选这个 -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>${jjwt.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@@ -0,0 +1,20 @@
|
||||
package com.accompany.oauth.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Data
|
||||
@Configuration
|
||||
@RefreshScope
|
||||
@ConfigurationProperties("oauth2.server")
|
||||
public class OAuthConfig {
|
||||
|
||||
private String clientId;
|
||||
|
||||
private String clientSecret;
|
||||
|
||||
private String jwtSignKey;
|
||||
|
||||
}
|
@@ -9,6 +9,7 @@ import com.accompany.oauth.model.UserDetails;
|
||||
import com.accompany.oauth.util.JwtUtil;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import org.redisson.api.RBucket;
|
||||
import com.accompany.oauth.ticket.RedisTicketStore;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -28,13 +29,14 @@ import java.util.concurrent.TimeUnit;
|
||||
@Component
|
||||
public class TokenManager {
|
||||
|
||||
@Autowired
|
||||
public JwtUtil jwtUtil;
|
||||
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
private RedissonClient redissonClient;
|
||||
|
||||
@Autowired
|
||||
private RedissonClient redissonClient;
|
||||
private RedisTicketStore redisTicketStore;
|
||||
|
||||
/**
|
||||
* 生成Token对
|
||||
@@ -54,6 +56,9 @@ public class TokenManager {
|
||||
|
||||
// 将token存储到Redis中
|
||||
storeTokenInRedis(accessToken, refreshToken, userDetails);
|
||||
|
||||
// 存储用户的access token到ticket store
|
||||
redisTicketStore.storeAccessToken(userDetails.getUserId(), accessToken, jwtUtil.getAccessTokenExpiration());
|
||||
|
||||
TokenPair tokenPair = new TokenPair();
|
||||
tokenPair.setAccessToken(accessToken);
|
||||
|
@@ -0,0 +1,192 @@
|
||||
package com.accompany.oauth.service;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.accompany.common.constant.Constant;
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import com.accompany.common.utils.CommonUtil;
|
||||
import com.accompany.common.utils.DateTimeUtil;
|
||||
import com.accompany.core.exception.ServiceException;
|
||||
import com.accompany.core.model.Account;
|
||||
import com.accompany.core.model.UserCancelRecord;
|
||||
import com.accompany.core.model.Users;
|
||||
import com.accompany.core.mybatismapper.AccountMapper;
|
||||
import com.accompany.core.service.SysConfService;
|
||||
import com.accompany.core.service.account.AccountService;
|
||||
import com.accompany.core.service.common.JedisService;
|
||||
import com.accompany.core.service.user.UserCancelRecordService;
|
||||
import com.accompany.core.service.user.UsersBaseService;
|
||||
import com.accompany.core.util.MD5;
|
||||
import com.accompany.email.service.EmailService;
|
||||
import com.accompany.sms.service.SmsService;
|
||||
import com.google.gson.Gson;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author liuguofu
|
||||
* on 2015/3/20.
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AccountManageService {
|
||||
|
||||
@Autowired
|
||||
private JedisService jedisService;
|
||||
@Autowired
|
||||
private AccountMapper accountMapper;
|
||||
@Autowired
|
||||
private AccountService accountService;
|
||||
@Autowired
|
||||
private UsersBaseService usersBaseService;
|
||||
@Autowired
|
||||
private UserCancelRecordService userCancelRecordService;
|
||||
@Autowired
|
||||
private SysConfService sysConfService;
|
||||
@Autowired
|
||||
private SmsService smsService;
|
||||
@Autowired
|
||||
private EmailService emailService;
|
||||
|
||||
protected Gson gson = new Gson();
|
||||
|
||||
/**
|
||||
* 重置密码
|
||||
* 两个场景调用 => 客户端未登录 忘记密码, 此时uid 为 null 登录状态下忘记密码 uid有值
|
||||
* @param uid
|
||||
* @param phone
|
||||
* @param password
|
||||
* @param resetCode
|
||||
* @return 1:成功 2:重置码无效 3:用户不存在
|
||||
*/
|
||||
public void resetPasswordByResetCode(Long uid, String phone, String password, String resetCode) {
|
||||
if (phone.contains("*")) {
|
||||
Account account = accountService.getById(uid);
|
||||
if (account == null) {
|
||||
throw new ServiceException(BusiStatus.USER_NOT_EXISTED);
|
||||
}
|
||||
phone = account.getPhone();
|
||||
if (!CommonUtil.checkPhoneFormat(account.getPhoneAreaCode(), account.getPhone())) {
|
||||
throw new ServiceException(BusiStatus.ACCOUNT_NOT_BIND_PHONE);
|
||||
}
|
||||
}
|
||||
|
||||
long count = accountService.countByPhone(phone);
|
||||
if (count > 1L) {
|
||||
throw new ServiceException(BusiStatus.PHONE_BIND_TOO_MANY_ACCOUNT);
|
||||
}
|
||||
|
||||
Account account = accountService.getAccountByPhone(phone);
|
||||
if (null == account || (uid != null && !account.getUid().equals(uid))) {
|
||||
throw new ServiceException(BusiStatus.PHONE_BIND_ERROR);
|
||||
}
|
||||
|
||||
//检验验证码
|
||||
if (!smsService.verifySmsCodeByCache(phone, resetCode)) {
|
||||
throw new ServiceException(BusiStatus.INVALID_IDENTIFYING_CODE);
|
||||
}
|
||||
|
||||
accountService.resetAccountPwd(account.getUid(), password);
|
||||
//成功后删除验证码缓存
|
||||
smsService.delSmsCodeCache(phone);
|
||||
|
||||
// 删除用户信息缓存
|
||||
jedisService.hdel(RedisKey.user.getKey(), account.getUid().toString());
|
||||
jedisService.hdel(RedisKey.user_summary.getKey(), account.getUid().toString());
|
||||
accountService.delNickPasswordCache(account.getErbanNo());
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置密码
|
||||
* 两个场景调用 => 客户端未登录 忘记密码, 此时uid 为 null 登录状态下忘记密码 uid有值
|
||||
* @param uid
|
||||
* @param email
|
||||
* @param password
|
||||
* @param code
|
||||
* @return 1:成功 2:重置码无效 3:用户不存在
|
||||
*/
|
||||
public void resetPasswordByEmailCode(Long uid, String email, String password, String code) {
|
||||
emailService.validEmailAddress(email);
|
||||
|
||||
long count = accountService.countByEmail(email);
|
||||
if (count > 1L) {
|
||||
throw new ServiceException(BusiStatus.EMAIL_BIND_TOO_MANY_ACCOUNT);
|
||||
}
|
||||
|
||||
Account account = accountService.getAccountByEmail(email);
|
||||
if (null == account) {
|
||||
throw new ServiceException(BusiStatus.ACCOUNT_NOT_BIND_EMAIL);
|
||||
} else if (null != uid && !account.getUid().equals(uid)) {
|
||||
throw new ServiceException(BusiStatus.ACCOUNT_BIND_EMAIL_DIFF);
|
||||
}
|
||||
|
||||
//检验验证码
|
||||
if (!emailService.verifyCodeByCache(email, code)) {
|
||||
throw new ServiceException(BusiStatus.INVALID_IDENTIFYING_EMAIL_CODE);
|
||||
}
|
||||
|
||||
accountService.resetAccountPwd(account.getUid(), password);
|
||||
//成功后删除验证码缓存
|
||||
emailService.delCodeCache(email);
|
||||
|
||||
// 删除用户信息缓存
|
||||
jedisService.hdel(RedisKey.user.getKey(), account.getUid().toString());
|
||||
jedisService.hdel(RedisKey.user_summary.getKey(), account.getUid().toString());
|
||||
accountService.delNickPasswordCache(account.getErbanNo());
|
||||
}
|
||||
|
||||
public void resetPasswordByOldPassword(String phone, String password, String newPassword) {
|
||||
Account account = accountService.getAccountByPhone(phone);
|
||||
if (null == account) {
|
||||
throw new ServiceException(BusiStatus.USER_NOT_EXISTED);
|
||||
}
|
||||
|
||||
String oldPwd = account.getPassword();
|
||||
password = encryptPassword(password);
|
||||
if (!StringUtils.hasText(password) || !password.equals(oldPwd)) {
|
||||
throw new ServiceException(BusiStatus.OLD_PASSWORD_ERROR);
|
||||
}
|
||||
|
||||
accountService.resetAccountPwd(account.getUid(), newPassword);
|
||||
accountService.delNickPasswordCache(account.getErbanNo());
|
||||
|
||||
//记录最近30天内绑定手机号
|
||||
jedisService.setex(RedisKey.modify_pwd_sign.getKey(String.valueOf(account.getUid())),
|
||||
30 * 24 * 60 * 60, String.valueOf(new Date().getTime()));
|
||||
}
|
||||
|
||||
private String encryptPassword(String password) {
|
||||
return MD5.getMD5(password);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置登录密码
|
||||
* @param uid
|
||||
* @param password
|
||||
* @return
|
||||
*/
|
||||
public void setupInitialPassword(Long uid, String password) {
|
||||
Account account = accountService.getById(uid);
|
||||
if (account == null) {
|
||||
throw new ServiceException(BusiStatus.INVALID_USER);
|
||||
}
|
||||
|
||||
Boolean result = accountService.updateAccountPwd(account.getUid(), password);
|
||||
if (!result) {
|
||||
throw new ServiceException(BusiStatus.INVALID_REQUEST);
|
||||
}
|
||||
|
||||
// 更新用户缓存
|
||||
this.jedisService.hdel(RedisKey.user.getKey(), account.getUid().toString());
|
||||
this.jedisService.hdel(RedisKey.user_summary.getKey(), account.getUid().toString());
|
||||
|
||||
accountService.delNickPasswordCache(account.getErbanNo());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
package com.accompany.oauth.service;
|
||||
|
||||
import com.accompany.oauth.constant.AuthType;
|
||||
import com.accompany.oauth.dto.AuthCredentials;
|
||||
import com.accompany.oauth.dto.AuthResult;
|
||||
import com.accompany.oauth.dto.UserInfo;
|
||||
@@ -105,6 +104,43 @@ public class AuthenticationService {
|
||||
tokenManager.revokeTokensByUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 第三方登录认证 (兼容OAuth2格式)
|
||||
*
|
||||
* @param openId 第三方OpenID
|
||||
* @param thirdPartyType 第三方类型
|
||||
* @param unionId UnionID(可选)
|
||||
* @param idToken ID Token(可选)
|
||||
* @param deviceInfo 设备信息
|
||||
* @param ipAddress IP地址
|
||||
* @param clientId 客户端标识
|
||||
* @return 认证结果
|
||||
* @throws AuthenticationException 认证失败
|
||||
*/
|
||||
public AuthResult authenticateByThirdParty(String openId, Integer thirdPartyType,
|
||||
String unionId, String idToken,
|
||||
Object deviceInfo, String ipAddress,
|
||||
String clientId) {
|
||||
try {
|
||||
// 1. 第三方认证
|
||||
UserDetails userDetails = userService.authenticateByOpenId(openId, thirdPartyType.byteValue());
|
||||
|
||||
// 2. 检查用户状态
|
||||
userService.checkUserStatus(userDetails);
|
||||
|
||||
// 3. 设置客户端信息
|
||||
userDetails.setClientId(clientId);
|
||||
|
||||
// 4. 生成Token
|
||||
TokenPair tokenPair = tokenManager.generateToken(userDetails);
|
||||
|
||||
// 5. 构建认证结果
|
||||
return buildAuthResult(tokenPair, userDetails);
|
||||
} catch (Exception e) {
|
||||
throw new AuthenticationException("第三方登录失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据认证类型进行用户认证
|
||||
*
|
||||
@@ -165,6 +201,10 @@ public class AuthenticationService {
|
||||
authResult.setUid(userDetails.getUserId());
|
||||
authResult.setNetEaseToken(""); // TODO: 需要根据实际业务填充
|
||||
authResult.setAccid(""); // TODO: 需要根据实际业务填充
|
||||
authResult.setUserToken(""); // TODO: 需要根据实际业务填充
|
||||
authResult.setLoginKey(""); // TODO: 需要根据实际业务填充
|
||||
authResult.setUserToken(""); // TODO: 需要根据实际业务填充
|
||||
authResult.setLoginKey(""); // TODO: 需要根据实际业务填充
|
||||
|
||||
return authResult;
|
||||
}
|
||||
|
@@ -0,0 +1,108 @@
|
||||
package com.accompany.oauth.ticket;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 默认Ticket实现
|
||||
* 迁移自OAuth2模块的DefaultTicket
|
||||
*
|
||||
* @author Accompany OAuth Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class DefaultTicket implements Ticket, Serializable {
|
||||
private String value;
|
||||
|
||||
private Date expiration;
|
||||
|
||||
private String ticketType = ONCE_TYPE.toLowerCase();
|
||||
|
||||
private String accessToken;
|
||||
|
||||
private Set<String> scope;
|
||||
|
||||
private Map<String, Object> additionalInformation = Collections.emptyMap();
|
||||
|
||||
/**
|
||||
* 创建票据
|
||||
*/
|
||||
public DefaultTicket(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制构造函数
|
||||
*/
|
||||
public DefaultTicket(Ticket ticket) {
|
||||
this(ticket.getValue());
|
||||
setAdditionalInformation(ticket.getAdditionalInformation());
|
||||
setAccessToken(ticket.getAccessToken());
|
||||
setExpiration(ticket.getExpiration());
|
||||
setScope(ticket.getScope());
|
||||
setTicketType(ticket.getTicketType());
|
||||
}
|
||||
|
||||
public void setAdditionalInformation(Map<String, Object> additionalInformation) {
|
||||
this.additionalInformation = new LinkedHashMap<>(additionalInformation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAdditionalInformation() {
|
||||
return additionalInformation;
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public void setTicketType(String ticketType) {
|
||||
this.ticketType = ticketType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTicketType() {
|
||||
return ticketType;
|
||||
}
|
||||
|
||||
public void setExpiration(Date expiration) {
|
||||
this.expiration = expiration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getExpiration() {
|
||||
return expiration;
|
||||
}
|
||||
|
||||
public void setExpiresIn(int delta) {
|
||||
setExpiration(new Date(System.currentTimeMillis() + delta * 1000L));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExpiresIn() {
|
||||
return expiration != null ? Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L)
|
||||
.intValue() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void setScope(Set<String> scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getScope() {
|
||||
return scope;
|
||||
}
|
||||
}
|
@@ -0,0 +1,94 @@
|
||||
package com.accompany.oauth.ticket;
|
||||
|
||||
import com.accompany.oauth.model.UserDetails;
|
||||
import com.accompany.oauth.util.JwtUtil;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.jsonwebtoken.JwtBuilder;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JWT票据增强器
|
||||
* 迁移自OAuth2模块的JwtTicketConverter
|
||||
*
|
||||
* @author Accompany OAuth Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Component
|
||||
public class JwtTicketEnhancer implements TicketEnhancer {
|
||||
|
||||
@Value("${oauth.jwt.secret:accompany-oauth-secret-key-for-jwt-token-generation}")
|
||||
private String secret;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public Ticket enhance(Ticket ticket, UserDetails userDetails) {
|
||||
DefaultTicket result = new DefaultTicket(ticket);
|
||||
result.setValue(encode(ticket, userDetails));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码票据为JWT
|
||||
*
|
||||
* @param ticket 票据
|
||||
* @param userDetails 用户详情
|
||||
* @return JWT字符串
|
||||
*/
|
||||
protected String encode(Ticket ticket, UserDetails userDetails) {
|
||||
try {
|
||||
Map<String, Object> claims = convertTicket(ticket, userDetails);
|
||||
|
||||
// 确保密钥长度足够
|
||||
String key = secret;
|
||||
if (key.getBytes(StandardCharsets.UTF_8).length < 32) {
|
||||
key = key + "0123456789abcdef0123456789abcdef";
|
||||
}
|
||||
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), SignatureAlgorithm.HS256.getJcaName());
|
||||
|
||||
Date now = new Date();
|
||||
Date expiration = ticket.getExpiration();
|
||||
|
||||
JwtBuilder builder = Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.setIssuedAt(now)
|
||||
.setExpiration(expiration)
|
||||
.signWith(secretKey, SignatureAlgorithm.HS256);
|
||||
|
||||
return builder.compact();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Cannot convert ticket to JWT", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换票据为Claims
|
||||
*
|
||||
* @param ticket 票据
|
||||
* @param userDetails 用户详情
|
||||
* @return Claims Map
|
||||
*/
|
||||
protected Map<String, Object> convertTicket(Ticket ticket, UserDetails userDetails) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("ticket_id", ticket.getValue());
|
||||
response.put("client_id", userDetails.getClientId() != null ? userDetails.getClientId() : "default");
|
||||
response.put("exp", ticket.getExpiresIn());
|
||||
response.put("uid", userDetails.getUserId());
|
||||
response.put("ticket_type", ticket.getTicketType());
|
||||
if (ticket.getScope() != null) {
|
||||
response.put("scope", String.join(" ", ticket.getScope()));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
package com.accompany.oauth.ticket;
|
||||
|
||||
import com.accompany.oauth.model.UserDetails;
|
||||
import org.redisson.api.RBucket;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Redis票据存储实现
|
||||
* 迁移自OAuth2模块的RedisTicketStore
|
||||
*
|
||||
* @author Accompany OAuth Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Component
|
||||
public class RedisTicketStore implements TicketStore {
|
||||
|
||||
private static final String TICKET_PREFIX = "oauth:ticket:";
|
||||
private static final String ACCESS_TOKEN_PREFIX = "oauth:uid_access_token:";
|
||||
|
||||
@Autowired
|
||||
private RedissonClient redissonClient;
|
||||
|
||||
@Override
|
||||
public void storeTicket(Ticket ticket, UserDetails userDetails) {
|
||||
Long uid = userDetails.getUserId();
|
||||
|
||||
// 存储用户的ticket
|
||||
String ticketKey = TICKET_PREFIX + uid;
|
||||
RBucket<String> ticketBucket = redissonClient.getBucket(ticketKey);
|
||||
ticketBucket.set(ticket.getValue(), ticket.getExpiresIn(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readTicket(String key) {
|
||||
String ticketKey = TICKET_PREFIX + key;
|
||||
RBucket<String> bucket = redissonClient.getBucket(ticketKey);
|
||||
return bucket.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readAccessToken(String key) {
|
||||
String accessTokenKey = ACCESS_TOKEN_PREFIX + key;
|
||||
RBucket<String> bucket = redissonClient.getBucket(accessTokenKey);
|
||||
return bucket.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储用户的access token
|
||||
*
|
||||
* @param uid 用户ID
|
||||
* @param accessToken 访问令牌
|
||||
* @param expiresIn 过期时间(秒)
|
||||
*/
|
||||
public void storeAccessToken(Long uid, String accessToken, long expiresIn) {
|
||||
String accessTokenKey = ACCESS_TOKEN_PREFIX + uid;
|
||||
RBucket<String> bucket = redissonClient.getBucket(accessTokenKey);
|
||||
bucket.set(accessToken, expiresIn, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package com.accompany.oauth.ticket;
|
||||
|
||||
import com.accompany.oauth.model.UserDetails;
|
||||
|
||||
/**
|
||||
* Ticket增强器接口
|
||||
* 迁移自OAuth2模块的TicketEnhancer
|
||||
*
|
||||
* @author Accompany OAuth Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface TicketEnhancer {
|
||||
|
||||
/**
|
||||
* 增强票据(如JWT签名)
|
||||
*
|
||||
* @param ticket 原始票据
|
||||
* @param userDetails 用户详情
|
||||
* @return 增强后的票据
|
||||
*/
|
||||
Ticket enhance(Ticket ticket, UserDetails userDetails);
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
package com.accompany.oauth.ticket;
|
||||
|
||||
import com.accompany.oauth.manager.TokenManager;
|
||||
import com.accompany.oauth.model.TokenValidation;
|
||||
import com.accompany.oauth.model.UserDetails;
|
||||
import com.accompany.oauth.service.UserService;
|
||||
import com.accompany.oauth.exception.TokenException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Ticket服务
|
||||
* 迁移自OAuth2模块的TicketServices,简化实现
|
||||
*
|
||||
* @author Accompany OAuth Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Service
|
||||
public class TicketService {
|
||||
|
||||
private final int ticketValiditySeconds = 60 * 60; // ticket过期时间1小时
|
||||
|
||||
@Autowired
|
||||
private TokenManager tokenManager;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private TicketStore ticketStore;
|
||||
|
||||
@Autowired
|
||||
private TicketEnhancer ticketEnhancer;
|
||||
|
||||
/**
|
||||
* 签发票据
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @return 票据响应
|
||||
*/
|
||||
public Map<String, Object> issueTicket(String accessToken) {
|
||||
// 1. 验证访问令牌
|
||||
TokenValidation validation = tokenManager.validateToken(accessToken);
|
||||
if (!validation.isValid()) {
|
||||
throw TokenException.invalidToken();
|
||||
}
|
||||
|
||||
// 2. 获取用户信息
|
||||
UserDetails userDetails = userService.getUserById(validation.getUserId());
|
||||
userService.checkUserStatus(userDetails);
|
||||
|
||||
// 3. 检查token缓存
|
||||
String uidStr = userDetails.getUserId().toString();
|
||||
String realAccessToken = ticketStore.readAccessToken(uidStr);
|
||||
if (realAccessToken == null || !realAccessToken.equals(accessToken)) {
|
||||
throw TokenException.invalidToken();
|
||||
}
|
||||
|
||||
// 4. 创建票据
|
||||
DefaultTicket defaultTicket = new DefaultTicket(UUID.randomUUID().toString());
|
||||
defaultTicket.setAccessToken(accessToken);
|
||||
defaultTicket.setExpiresIn(ticketValiditySeconds);
|
||||
defaultTicket.setTicketType(Ticket.ONCE_TYPE);
|
||||
defaultTicket.setScope(validation.getScopes());
|
||||
|
||||
// 5. 增强票据(JWT签名)
|
||||
Ticket enhancedTicket = ticketEnhancer.enhance(defaultTicket, userDetails);
|
||||
|
||||
// 6. 存储票据
|
||||
ticketStore.storeTicket(enhancedTicket, userDetails);
|
||||
|
||||
// 7. 构建响应
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("tickets", Arrays.asList(createTicketVo(enhancedTicket)));
|
||||
response.put("uid", userDetails.getUserId());
|
||||
response.put("issue_type", Ticket.ONCE_TYPE);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建票据VO
|
||||
*
|
||||
* @param ticket 票据
|
||||
* @return 票据VO
|
||||
*/
|
||||
private Map<String, Object> createTicketVo(Ticket ticket) {
|
||||
Map<String, Object> ticketVo = new HashMap<>();
|
||||
ticketVo.put("ticket", ticket.getValue());
|
||||
ticketVo.put("expires_in", ticket.getExpiresIn());
|
||||
ticketVo.put("ticket_type", ticket.getTicketType());
|
||||
ticketVo.put("scope", ticket.getScope() != null ? String.join(" ", ticket.getScope()) : "");
|
||||
return ticketVo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存登录记录(异步)
|
||||
*
|
||||
* @param uid 用户ID
|
||||
* @param ipAddress IP地址
|
||||
* @param deviceInfo 设备信息
|
||||
*/
|
||||
public void saveLoginRecord(Long uid, String ipAddress, Object deviceInfo) {
|
||||
// TODO: 实现登录记录保存逻辑
|
||||
// 这里需要根据具体的业务需求来实现
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package com.accompany.oauth.ticket;
|
||||
|
||||
import com.accompany.oauth.model.UserDetails;
|
||||
|
||||
/**
|
||||
* Ticket存储接口
|
||||
* 迁移自OAuth2模块的TicketStore
|
||||
*
|
||||
* @author Accompany OAuth Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface TicketStore {
|
||||
|
||||
/**
|
||||
* 存储票据
|
||||
*
|
||||
* @param ticket 票据
|
||||
* @param userDetails 用户详情
|
||||
*/
|
||||
void storeTicket(Ticket ticket, UserDetails userDetails);
|
||||
|
||||
/**
|
||||
* 读取票据
|
||||
*
|
||||
* @param key 票据键
|
||||
* @return 票据值
|
||||
*/
|
||||
String readTicket(String key);
|
||||
|
||||
/**
|
||||
* 读取访问令牌
|
||||
*
|
||||
* @param key 用户键
|
||||
* @return 访问令牌值
|
||||
*/
|
||||
String readAccessToken(String key);
|
||||
}
|
@@ -10,7 +10,6 @@ import javax.annotation.PostConstruct;
|
||||
import javax.crypto.SecretKey;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -43,7 +42,7 @@ public class JwtUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成访问令牌
|
||||
* 生成访问令牌 (兼容OAuth2格式)
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param clientId 客户端ID
|
||||
@@ -60,10 +59,16 @@ public class JwtUtil {
|
||||
.setExpiration(expiration)
|
||||
.claim("client_id", clientId)
|
||||
.claim("token_type", "access_token")
|
||||
.claim("uid", userId) // 兼容OAuth2
|
||||
.claim("user_name", userId.toString()) // 兼容OAuth2
|
||||
.claim("authorities", "oauth2") // 兼容OAuth2
|
||||
.claim("jti", generateJti()) // 兼容OAuth2
|
||||
.signWith(secretKey, SignatureAlgorithm.HS256);
|
||||
|
||||
if (scopes != null && !scopes.isEmpty()) {
|
||||
builder.claim("scope", String.join(" ", scopes));
|
||||
} else {
|
||||
builder.claim("scope", "read write"); // 默认权限
|
||||
}
|
||||
|
||||
return builder.compact();
|
||||
@@ -99,7 +104,7 @@ public class JwtUtil {
|
||||
*/
|
||||
public Claims validateAndParseToken(String token) {
|
||||
try {
|
||||
return Jwts.parserBuilder()
|
||||
return Jwts.parser()
|
||||
.setSigningKey(secretKey)
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
@@ -187,4 +192,13 @@ public class JwtUtil {
|
||||
public long getRefreshTokenExpiration() {
|
||||
return refreshTokenExpiration;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成JWT Token ID (兼容OAuth2)
|
||||
*
|
||||
* @return JTI
|
||||
*/
|
||||
private String generateJti() {
|
||||
return java.util.UUID.randomUUID().toString().replace("-", "");
|
||||
}
|
||||
}
|
@@ -9,8 +9,9 @@ import org.springframework.boot.jackson.JsonComponent;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* AuthResult自定义序列化器 - 兼容OAuth2的CustomOAuth2AccessToken格式
|
||||
* 输出格式:{code:200, data:{uid, access_token, token_type, expires_in, ...}}
|
||||
* AuthResult自定义序列化器 - 直接输出OAuth2标准格式
|
||||
* 输出格式:{uid, access_token, token_type, expires_in, netEaseToken, userToken, loginKey, ...}
|
||||
* 直接兼容OAuth2的CustomOAuth2AccessToken格式
|
||||
*
|
||||
* @author Accompany OAuth Team
|
||||
* @since 1.0.0
|
||||
@@ -24,23 +25,6 @@ public class AuthResultJsonSerializer extends JsonSerializer<AuthResult> {
|
||||
|
||||
gen.writeStartObject();
|
||||
|
||||
// 写入状态码
|
||||
gen.writeNumberField("code", 200);
|
||||
|
||||
// 写入data对象
|
||||
gen.writeObjectFieldStart("data");
|
||||
|
||||
// 用户ID (兼容oauth2)
|
||||
gen.writeNumberField("uid", authResult.getUid());
|
||||
|
||||
// 网易云信Token (兼容oauth2)
|
||||
gen.writeStringField("netEaseToken", authResult.getNetEaseToken());
|
||||
|
||||
// accid (兼容oauth2)
|
||||
if (authResult.getAccid() != null) {
|
||||
gen.writeStringField("accid", authResult.getAccid());
|
||||
}
|
||||
|
||||
// 标准OAuth字段
|
||||
gen.writeStringField("access_token", authResult.getAccessToken());
|
||||
gen.writeStringField("token_type", authResult.getTokenType());
|
||||
@@ -57,7 +41,31 @@ public class AuthResultJsonSerializer extends JsonSerializer<AuthResult> {
|
||||
gen.writeStringField("scope", authResult.getScope());
|
||||
}
|
||||
|
||||
gen.writeEndObject(); // end data
|
||||
gen.writeEndObject(); // end root
|
||||
// 用户ID (兼容oauth2)
|
||||
if (authResult.getUid() != null) {
|
||||
gen.writeNumberField("uid", authResult.getUid());
|
||||
}
|
||||
|
||||
// 网易云信Token (兼容oauth2)
|
||||
if (authResult.getNetEaseToken() != null && !authResult.getNetEaseToken().isEmpty()) {
|
||||
gen.writeStringField("netEaseToken", authResult.getNetEaseToken());
|
||||
}
|
||||
|
||||
// accid (兼容oauth2)
|
||||
if (authResult.getAccid() != null && !authResult.getAccid().isEmpty()) {
|
||||
gen.writeStringField("accid", authResult.getAccid());
|
||||
}
|
||||
|
||||
// userToken (兼容oauth2)
|
||||
if (authResult.getUserToken() != null && !authResult.getUserToken().isEmpty()) {
|
||||
gen.writeStringField("userToken", authResult.getUserToken());
|
||||
}
|
||||
|
||||
// loginKey (兼容oauth2)
|
||||
if (authResult.getLoginKey() != null && !authResult.getLoginKey().isEmpty()) {
|
||||
gen.writeStringField("loginKey", authResult.getLoginKey());
|
||||
}
|
||||
|
||||
gen.writeEndObject();
|
||||
}
|
||||
}
|
@@ -3,9 +3,15 @@ package com.accompany.oauth.controller;
|
||||
import com.accompany.common.constant.AppEnum;
|
||||
import com.accompany.common.device.DeviceInfo;
|
||||
import com.accompany.common.result.BusiResult;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import com.accompany.common.utils.DESUtils;
|
||||
import com.accompany.core.base.UidContextHolder;
|
||||
import com.accompany.core.util.KeyStore;
|
||||
import com.accompany.oauth.dto.AuthResult;
|
||||
import com.accompany.oauth.model.TokenValidation;
|
||||
import com.accompany.oauth.service.AccountManageService;
|
||||
import com.accompany.oauth.service.AuthenticationService;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@@ -23,10 +29,15 @@ import java.util.Map;
|
||||
@RestController
|
||||
@RequestMapping("/acc")
|
||||
public class AccountController {
|
||||
|
||||
|
||||
/** 密码强度检查正则,必须包括大小写字母和数字,长度为6到16 */
|
||||
private static final String PASSWORD_REGIX_V2 = "^(?=.*\\d)(?=.*[a-zA-Z]).{6,16}$";
|
||||
|
||||
@Autowired
|
||||
private AuthenticationService authenticationService;
|
||||
|
||||
@Autowired
|
||||
private AccountManageService accountManageService;
|
||||
|
||||
/**
|
||||
* 用户注销 (兼容OAuth2格式)
|
||||
*
|
||||
@@ -71,17 +82,29 @@ public class AccountController {
|
||||
|
||||
/**
|
||||
* 重置密码 (兼容OAuth2格式)
|
||||
*
|
||||
* @param requestBody 重置密码请求
|
||||
*
|
||||
* @return BusiResult响应结果
|
||||
*/
|
||||
@SneakyThrows
|
||||
@PostMapping("/pwd/reset")
|
||||
public BusiResult<Void> resetPassword(@RequestBody Map<String, Object> requestBody) {
|
||||
public BusiResult<Void> resetPassword(HttpServletRequest request,
|
||||
String phone, String newPwd, String smsCode) {
|
||||
// TODO: 实现密码重置逻辑
|
||||
// 1. 验证用户身份(手机号+验证码或邮箱+验证码)
|
||||
// 2. 重置密码
|
||||
// 3. 发送通知
|
||||
|
||||
|
||||
Long uid = UidContextHolder.get();
|
||||
phone = DESUtils.DESAndBase64Decrypt(phone, KeyStore.DES_ENCRYPT_KEY);
|
||||
newPwd = DESUtils.DESAndBase64Decrypt(newPwd, KeyStore.DES_ENCRYPT_KEY);
|
||||
|
||||
// 密码长度检查
|
||||
if(!newPwd.matches(PASSWORD_REGIX_V2)){
|
||||
return new BusiResult<>(BusiStatus.WEAK_PASSWORD);
|
||||
}
|
||||
|
||||
accountManageService.resetPasswordByResetCode(uid, phone, newPwd, smsCode);
|
||||
|
||||
throw new UnsupportedOperationException("密码重置功能暂未实现");
|
||||
}
|
||||
|
||||
|
@@ -1,19 +1,22 @@
|
||||
package com.accompany.oauth.controller;
|
||||
|
||||
import com.accompany.common.result.BusiResult;
|
||||
import com.accompany.oauth.constant.AuthType;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import com.accompany.common.utils.IPUtils;
|
||||
import com.accompany.core.base.DeviceInfoContextHolder;
|
||||
import com.accompany.oauth.constant.GrantTypeEnum;
|
||||
import com.accompany.oauth.constant.OAuthConstants;
|
||||
import com.accompany.oauth.dto.AuthCredentials;
|
||||
import com.accompany.oauth.dto.AuthResult;
|
||||
import com.accompany.oauth.dto.TokenRequest;
|
||||
import com.accompany.oauth.dto.H5AccessToken;
|
||||
import com.accompany.oauth.dto.DeviceInfo;
|
||||
import com.accompany.common.device.DeviceInfo;
|
||||
import com.accompany.oauth.service.AuthenticationService;
|
||||
import com.accompany.oauth.ticket.TicketService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -29,21 +32,50 @@ public class OAuthController {
|
||||
@Autowired
|
||||
private AuthenticationService authenticationService;
|
||||
|
||||
@Autowired
|
||||
private TicketService ticketService;
|
||||
|
||||
/**
|
||||
* 统一认证端点 - 获取Token (兼容OAuth2格式)
|
||||
* 支持表单提交和JSON提交两种方式
|
||||
*
|
||||
* @param request Token请求
|
||||
* @param httpRequest HTTP请求
|
||||
* @param grantType 授权类型
|
||||
* @param phone 手机号/用户名
|
||||
* @param email 邮箱
|
||||
* @param password 密码
|
||||
* @param code 验证码
|
||||
* @param clientId 客户端ID
|
||||
* @param clientSecret 客户端密钥
|
||||
* @param phoneAreaCode 电话区号
|
||||
* @return 直接返回AuthResult结构,兼容OAuth2的CustomOAuth2AccessToken
|
||||
*/
|
||||
@PostMapping("/token")
|
||||
public AuthResult token(@RequestBody TokenRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
public AuthResult token(@RequestParam(value = "grant_type", required = false) String grantType,
|
||||
@RequestParam(value = "client_id", required = false) String clientId,
|
||||
@RequestParam(value = "client_secret", required = false) String clientSecret,
|
||||
@RequestParam(value = "phone", required = false) String phone,
|
||||
@RequestParam(value = "phoneAreaCode", required = false) String phoneAreaCode,
|
||||
@RequestParam(value = "password", required = false) String password,
|
||||
@RequestParam(value = "email", required = false) String email,
|
||||
@RequestParam(value = "code", required = false) String code) {
|
||||
|
||||
DeviceInfo deviceInfo = DeviceInfoContextHolder.get();
|
||||
|
||||
// 支持两种格式:表单提交和JSON提交
|
||||
TokenRequest request = new TokenRequest();
|
||||
request.setGrantType(grantType);
|
||||
request.setUsername(StringUtils.hasText(phone) ? phone : email);
|
||||
request.setPassword(password);
|
||||
request.setCode(code);
|
||||
request.setClientId(clientId);
|
||||
request.setClientSecret(clientSecret);
|
||||
request.setDeviceId(deviceInfo.getDeviceId());
|
||||
|
||||
// 1. 验证请求参数
|
||||
validateTokenRequest(request);
|
||||
|
||||
// 2. 构建认证凭据
|
||||
AuthCredentials credentials = buildAuthCredentials(request, httpRequest);
|
||||
AuthCredentials credentials = buildAuthCredentials(request, phoneAreaCode, deviceInfo);
|
||||
|
||||
// 3. 执行认证
|
||||
AuthResult authResult = authenticationService.authenticate(credentials);
|
||||
@@ -71,39 +103,34 @@ public class OAuthController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销Token端点 (兼容OAuth2格式)
|
||||
* Ticket签发端点 (兼容OAuth2格式)
|
||||
*
|
||||
* @param token 要撤销的Token
|
||||
* @return BusiResult响应结果
|
||||
*/
|
||||
@PostMapping("/revoke")
|
||||
public BusiResult<Void> revoke(@RequestParam("token") String token) {
|
||||
if (!StringUtils.hasText(token)) {
|
||||
throw new IllegalArgumentException("缺少Token参数");
|
||||
}
|
||||
|
||||
authenticationService.revokeToken(token);
|
||||
|
||||
return BusiResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* H5授权登录端点 (兼容OAuth2格式)
|
||||
*
|
||||
* @param request Token请求
|
||||
* @param issueType 签发类型
|
||||
* @param accessToken 访问令牌
|
||||
* @param httpRequest HTTP请求
|
||||
* @return BusiResult包装的H5AccessToken
|
||||
* @return BusiResult包装的Ticket响应
|
||||
*/
|
||||
@PostMapping("/h5/token")
|
||||
public BusiResult<H5AccessToken> h5Token(@RequestBody TokenRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
// 执行认证
|
||||
AuthResult authResult = token(request, httpRequest);
|
||||
|
||||
// 转换为H5格式
|
||||
H5AccessToken h5Token = H5AccessToken.fromAuthResult(authResult);
|
||||
|
||||
return BusiResult.success(h5Token);
|
||||
@GetMapping("/ticket")
|
||||
public BusiResult<Map<String, Object>> issueTicket(@RequestParam("issue_type") String issueType,
|
||||
@RequestParam("access_token") String accessToken,
|
||||
HttpServletRequest httpRequest) {
|
||||
try {
|
||||
if (!"once".equals(issueType) && !"multi".equals(issueType)) {
|
||||
throw new IllegalArgumentException("unsupported ticket issue type");
|
||||
}
|
||||
|
||||
Map<String, Object> result = ticketService.issueTicket(accessToken);
|
||||
|
||||
// 获取IP地址并保存登录记录
|
||||
String ipAddress = IPUtils.getRealIpAddress(httpRequest);
|
||||
Long uid = (Long) result.get("uid");
|
||||
|
||||
ticketService.saveLoginRecord(uid, ipAddress, null);
|
||||
|
||||
return BusiResult.success(result);
|
||||
} catch (Exception e) {
|
||||
return BusiResult.fail(BusiStatus.IP_REGION_HAD_LIMIT, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,69 +144,51 @@ public class OAuthController {
|
||||
}
|
||||
|
||||
if (!StringUtils.hasText(request.getUsername()) &&
|
||||
!OAuthConstants.GrantType.REFRESH_TOKEN.equals(request.getGrantType())) {
|
||||
!OAuthConstants.GrantType.REFRESH_TOKEN.equals(request.getGrantType()) &&
|
||||
!OAuthConstants.GrantType.EMAIL.equals(request.getGrantType())) {
|
||||
throw new IllegalArgumentException("缺少username参数");
|
||||
}
|
||||
|
||||
if (!StringUtils.hasText(request.getPassword()) &&
|
||||
!OAuthConstants.GrantType.REFRESH_TOKEN.equals(request.getGrantType())) {
|
||||
!OAuthConstants.GrantType.REFRESH_TOKEN.equals(request.getGrantType()) &&
|
||||
!OAuthConstants.GrantType.EMAIL.equals(request.getGrantType())) {
|
||||
throw new IllegalArgumentException("缺少password参数");
|
||||
}
|
||||
|
||||
if (!StringUtils.hasText(request.getCode()) &&
|
||||
OAuthConstants.GrantType.EMAIL.equals(request.getGrantType())) {
|
||||
throw new IllegalArgumentException("缺少验证码参数");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建认证凭据
|
||||
*
|
||||
* @param request Token请求
|
||||
* @param httpRequest HTTP请求
|
||||
* @param phoneAreaCode 电话区号
|
||||
* @return 认证凭据
|
||||
*/
|
||||
private AuthCredentials buildAuthCredentials(TokenRequest request, HttpServletRequest httpRequest) {
|
||||
private AuthCredentials buildAuthCredentials(TokenRequest request,
|
||||
String phoneAreaCode, DeviceInfo deviceInfo) {
|
||||
AuthCredentials credentials = new AuthCredentials();
|
||||
|
||||
// 设置认证类型
|
||||
AuthType authType = AuthType.fromCode(request.getGrantType());
|
||||
credentials.setType(authType);
|
||||
GrantTypeEnum grantTypeEnum = GrantTypeEnum.fromCode(request.getGrantType());
|
||||
credentials.setType(grantTypeEnum);
|
||||
|
||||
// 设置主体和凭据
|
||||
credentials.setPrincipal(request.getUsername());
|
||||
credentials.setCredentials(request.getPassword());
|
||||
|
||||
|
||||
// 设置客户端信息
|
||||
credentials.setClientId(StringUtils.hasText(request.getClientId()) ?
|
||||
request.getClientId() : "default");
|
||||
|
||||
credentials.setClientId(request.getClientId());
|
||||
|
||||
// 设置权限范围
|
||||
credentials.setScope(StringUtils.hasText(request.getScope()) ?
|
||||
request.getScope() : OAuthConstants.Scope.ALL);
|
||||
credentials.setScope(OAuthConstants.Scope.ALL);
|
||||
|
||||
// 构建设备信息
|
||||
DeviceInfo deviceInfo = new DeviceInfo();
|
||||
deviceInfo.setDeviceId(request.getDeviceId());
|
||||
deviceInfo.setIpAddress(getClientIpAddress(httpRequest));
|
||||
deviceInfo.setUserAgent(httpRequest.getHeader("User-Agent"));
|
||||
credentials.setDeviceInfo(deviceInfo);
|
||||
|
||||
|
||||
return credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端IP地址
|
||||
*
|
||||
* @param request HTTP请求
|
||||
* @return IP地址
|
||||
*/
|
||||
private String getClientIpAddress(HttpServletRequest request) {
|
||||
String xForwardedFor = request.getHeader("X-Forwarded-For");
|
||||
if (StringUtils.hasText(xForwardedFor)) {
|
||||
return xForwardedFor.split(",")[0].trim();
|
||||
}
|
||||
|
||||
String xRealIp = request.getHeader("X-Real-IP");
|
||||
if (StringUtils.hasText(xRealIp)) {
|
||||
return xRealIp;
|
||||
}
|
||||
|
||||
return request.getRemoteAddr();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,133 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true" scanPeriod="60 seconds">
|
||||
<!--引入默认的一些设置-->
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
|
||||
<!--web信息-->
|
||||
<logger name="org.springframework.web" level="info"/>
|
||||
|
||||
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
|
||||
<property name="LOG_HOME" value="/data/java/weblog/accompany-oauth"/>
|
||||
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%X{trace_uuid}] %-5level %logger{36} %line - %msg%n" />
|
||||
|
||||
<!--写入日志到控制台的appender,用默认的,但是要去掉charset,否则windows下tomcat下乱码-->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="info_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_HOME}/web_info.log</file>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_HOME}/rolling/web_info.%d{yyyy-MM-dd}.%i.log.gz
|
||||
</fileNamePattern>
|
||||
<maxHistory>90</maxHistory>
|
||||
<cleanHistoryOnStart>true</cleanHistoryOnStart>
|
||||
<timeBasedFileNamingAndTriggeringPolicy
|
||||
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>256MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<charset>UTF-8</charset>
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="warn_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_HOME}/web_warn.log</file>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>WARN</level>
|
||||
</filter>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_HOME}/rolling/web_warn_.%d{yyyy-MM-dd}.%i.log.gz
|
||||
</fileNamePattern>
|
||||
<maxHistory>90</maxHistory>
|
||||
<cleanHistoryOnStart>true</cleanHistoryOnStart>
|
||||
<timeBasedFileNamingAndTriggeringPolicy
|
||||
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>256MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<charset>UTF-8</charset>
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="error_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_HOME}/web_error.log</file>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>ERROR</level>
|
||||
</filter>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_HOME}/rolling/web_error_.%d{yyyy-MM-dd}.%i.log.gz
|
||||
</fileNamePattern>
|
||||
<maxHistory>90</maxHistory>
|
||||
<cleanHistoryOnStart>true</cleanHistoryOnStart>
|
||||
<timeBasedFileNamingAndTriggeringPolicy
|
||||
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>256MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<charset>UTF-8</charset>
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!--异步到文件-->
|
||||
<appender name="info_async_file" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<queueSize>512</queueSize>
|
||||
<appender-ref ref="info_file"/>
|
||||
</appender>
|
||||
|
||||
<appender name ="warn_async_file" class= "ch.qos.logback.classic.AsyncAppender">
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<queueSize>512</queueSize>
|
||||
<includeCallerData>false</includeCallerData>
|
||||
<appender-ref ref ="warn_file"/>
|
||||
</appender>
|
||||
|
||||
<appender name ="error_async_file" class= "ch.qos.logback.classic.AsyncAppender">
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<queueSize>512</queueSize>
|
||||
<includeCallerData>false</includeCallerData>
|
||||
<appender-ref ref ="error_file"/>
|
||||
</appender>
|
||||
|
||||
<!--生产环境:打印控制台和输出到文件-->
|
||||
<springProfile name="prod">
|
||||
<root level="info">
|
||||
<appender-ref ref="info_async_file"/>
|
||||
<appender-ref ref="warn_async_file"/>
|
||||
<appender-ref ref="error_async_file"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
<!--开发环境:打印控制台-->
|
||||
<springProfile name="dev">
|
||||
<logger name="com.accompany" level="DEBUG"/>
|
||||
<root level="info">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="info_async_file"/>
|
||||
<appender-ref ref="warn_async_file"/>
|
||||
<appender-ref ref="error_async_file"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
<springProfile name="native">
|
||||
<logger name="com.accompany" level="DEBUG"/>
|
||||
<root level="info">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="info_async_file"/>
|
||||
<appender-ref ref="warn_async_file"/>
|
||||
<appender-ref ref="error_async_file"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
</configuration>
|
@@ -0,0 +1,195 @@
|
||||
package com.accompany.oauth;
|
||||
|
||||
import com.accompany.oauth.dto.AuthResult;
|
||||
import com.accompany.oauth.dto.TokenRequest;
|
||||
import com.accompany.oauth.controller.OAuthController;
|
||||
import com.accompany.oauth.controller.AccountController;
|
||||
import com.accompany.common.result.BusiResult;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* OAuth2兼容性集成测试
|
||||
* 验证OAuth模块与OAuth2模块的API兼容性
|
||||
*
|
||||
* @author Accompany OAuth Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SpringBootTest
|
||||
@ActiveProfiles("test")
|
||||
public class OAuth2CompatibilityIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private OAuthController oauthController;
|
||||
|
||||
@Autowired
|
||||
private AccountController accountController;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* 测试OAuth2 Token端点兼容性 - 表单格式
|
||||
*/
|
||||
@Test
|
||||
public void testOAuth2TokenEndpoint_FormFormat() {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setRemoteAddr("127.0.0.1");
|
||||
|
||||
try {
|
||||
AuthResult result = oauthController.token(
|
||||
"password", // grant_type
|
||||
"testuser", // phone
|
||||
null, // email
|
||||
"password123", // password
|
||||
null, // code
|
||||
"test-client", // client_id
|
||||
"read write", // scope
|
||||
"device-001"
|
||||
);
|
||||
|
||||
// 验证OAuth2兼容的响应结构
|
||||
assertNotNull(result);
|
||||
assertNotNull(result.getAccessToken());
|
||||
assertNotNull(result.getRefreshToken());
|
||||
assertNotNull(result.getTokenType());
|
||||
assertEquals("bearer", result.getTokenType().toLowerCase());
|
||||
assertTrue(result.getExpiresIn() > 0);
|
||||
|
||||
// 验证OAuth2特有字段
|
||||
assertNotNull(result.getUserToken());
|
||||
assertNotNull(result.getLoginKey());
|
||||
|
||||
System.out.println("OAuth2 Token Response: " + objectMapper.writeValueAsString(result));
|
||||
|
||||
} catch (Exception e) {
|
||||
// 在测试环境中,由于没有真实的用户服务实现,预期会抛出异常
|
||||
assertTrue(e.getMessage().contains("用户") || e.getMessage().contains("User") ||
|
||||
e.getMessage().contains("认证") || e.getMessage().contains("authentication"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试账户管理端点兼容性
|
||||
*/
|
||||
@Test
|
||||
public void testAccountEndpoints() {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setRemoteAddr("127.0.0.1");
|
||||
|
||||
try {
|
||||
// 测试登出端点
|
||||
BusiResult<Void> logoutResult = accountController.logout("mock-token");
|
||||
|
||||
} catch (Exception e) {
|
||||
assertTrue(e.getMessage().contains("token") || e.getMessage().contains("Token") ||
|
||||
e.getMessage().contains("无效") || e.getMessage().contains("invalid"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试Ticket端点兼容性
|
||||
*/
|
||||
@Test
|
||||
public void testTicketEndpoint() {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setRemoteAddr("127.0.0.1");
|
||||
|
||||
try {
|
||||
BusiResult<Map<String, Object>> ticketResult = oauthController.issueTicket(
|
||||
"once", "mock-access-token", request
|
||||
);
|
||||
|
||||
} catch (Exception e) {
|
||||
// 预期异常,因为token无效
|
||||
assertTrue(e.getMessage().contains("token") || e.getMessage().contains("Token") ||
|
||||
e.getMessage().contains("无效") || e.getMessage().contains("invalid"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试AuthResult的JSON序列化兼容性
|
||||
*/
|
||||
@Test
|
||||
public void testAuthResultSerialization() throws Exception {
|
||||
AuthResult authResult = new AuthResult();
|
||||
authResult.setAccessToken("test-access-token");
|
||||
authResult.setRefreshToken("test-refresh-token");
|
||||
authResult.setTokenType("bearer");
|
||||
authResult.setExpiresIn(3600L);
|
||||
authResult.setScope("read write");
|
||||
authResult.setUserToken("user-token-123");
|
||||
authResult.setLoginKey("login-key-456");
|
||||
|
||||
String json = objectMapper.writeValueAsString(authResult);
|
||||
System.out.println("AuthResult JSON: " + json);
|
||||
|
||||
// 验证JSON包含OAuth2标准字段
|
||||
assertTrue(json.contains("access_token"));
|
||||
assertTrue(json.contains("refresh_token"));
|
||||
assertTrue(json.contains("token_type"));
|
||||
assertTrue(json.contains("expires_in"));
|
||||
assertTrue(json.contains("scope"));
|
||||
|
||||
// 验证兼容字段
|
||||
assertTrue(json.contains("user_token"));
|
||||
assertTrue(json.contains("login_key"));
|
||||
|
||||
// 验证反序列化
|
||||
AuthResult deserializedResult = objectMapper.readValue(json, AuthResult.class);
|
||||
assertEquals(authResult.getAccessToken(), deserializedResult.getAccessToken());
|
||||
assertEquals(authResult.getRefreshToken(), deserializedResult.getRefreshToken());
|
||||
assertEquals(authResult.getTokenType(), deserializedResult.getTokenType());
|
||||
assertEquals(authResult.getExpiresIn(), deserializedResult.getExpiresIn());
|
||||
assertEquals(authResult.getUserToken(), deserializedResult.getUserToken());
|
||||
assertEquals(authResult.getLoginKey(), deserializedResult.getLoginKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试Token请求参数验证
|
||||
*/
|
||||
@Test
|
||||
public void testTokenRequestValidation() {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setRemoteAddr("127.0.0.1");
|
||||
|
||||
// 测试缺少grant_type
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
oauthController.token(null, "user", null, "pass", null, null, null, null);
|
||||
});
|
||||
|
||||
// 测试缺少username
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
oauthController.token("password", null, null, null, null, null, null, null);
|
||||
});
|
||||
|
||||
// 测试缺少password
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
oauthController.token("password", "user", null, null, null, null, null, null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证支持的grant_type类型
|
||||
*/
|
||||
@Test
|
||||
public void testSupportedGrantTypes() {
|
||||
String[] supportedTypes = {"password", "sms_code", "email_code", "third_party", "refresh_token"};
|
||||
|
||||
for (String grantType : supportedTypes) {
|
||||
TokenRequest request = new TokenRequest();
|
||||
request.setGrantType(grantType);
|
||||
|
||||
assertNotNull(request.getGrantType());
|
||||
assertEquals(grantType, request.getGrantType());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,155 @@
|
||||
package com.accompany.oauth.integration;
|
||||
|
||||
import com.accompany.oauth.dto.AuthResult;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* OAuth2兼容性集成测试
|
||||
* 验证OAuth模块与OAuth2模块的API兼容性
|
||||
*
|
||||
* @author Accompany OAuth Team
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
public class OAuth2CompatibilityTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* 测试密码认证端点兼容性 (表单格式)
|
||||
*/
|
||||
@Test
|
||||
public void testPasswordAuthenticationForm() throws Exception {
|
||||
MvcResult result = mockMvc.perform(post("/oauth/token")
|
||||
.param("grant_type", "password")
|
||||
.param("phone", "13800138000")
|
||||
.param("password", "123456")
|
||||
.param("client_id", "erban-client")
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.access_token").exists())
|
||||
.andExpect(jsonPath("$.token_type").value("Bearer"))
|
||||
.andExpect(jsonPath("$.expires_in").exists())
|
||||
.andExpect(jsonPath("$.uid").exists())
|
||||
.andReturn();
|
||||
|
||||
String responseJson = result.getResponse().getContentAsString();
|
||||
AuthResult authResult = objectMapper.readValue(responseJson, AuthResult.class);
|
||||
|
||||
// 验证OAuth2兼容字段
|
||||
assertNotNull(authResult.getAccessToken());
|
||||
assertEquals("Bearer", authResult.getTokenType());
|
||||
assertNotNull(authResult.getExpiresIn());
|
||||
assertNotNull(authResult.getUid());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试验证码认证端点兼容性
|
||||
*/
|
||||
@Test
|
||||
public void testVerifyCodeAuthentication() throws Exception {
|
||||
mockMvc.perform(post("/oauth/token")
|
||||
.param("grant_type", "verify_code")
|
||||
.param("phone", "13800138000")
|
||||
.param("code", "888888")
|
||||
.param("client_id", "erban-client")
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.access_token").exists())
|
||||
.andExpect(jsonPath("$.token_type").value("Bearer"))
|
||||
.andExpect(jsonPath("$.uid").exists());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试邮箱认证端点兼容性
|
||||
*/
|
||||
@Test
|
||||
public void testEmailAuthentication() throws Exception {
|
||||
mockMvc.perform(post("/oauth/token")
|
||||
.param("grant_type", "email")
|
||||
.param("email", "test@example.com")
|
||||
.param("code", "666666")
|
||||
.param("client_id", "erban-client")
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.access_token").exists())
|
||||
.andExpect(jsonPath("$.token_type").value("Bearer"))
|
||||
.andExpect(jsonPath("$.uid").exists());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试Token响应格式兼容性
|
||||
* 确保返回的JSON结构与OAuth2的CustomOAuth2AccessToken一致
|
||||
*/
|
||||
@Test
|
||||
public void testTokenResponseFormat() throws Exception {
|
||||
MvcResult result = mockMvc.perform(post("/oauth/token")
|
||||
.param("grant_type", "password")
|
||||
.param("phone", "13800138000")
|
||||
.param("password", "123456")
|
||||
.param("client_id", "erban-client")
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn();
|
||||
|
||||
String responseJson = result.getResponse().getContentAsString();
|
||||
|
||||
// 验证JSON包含所有OAuth2字段
|
||||
assertTrue(responseJson.contains("access_token"));
|
||||
assertTrue(responseJson.contains("token_type"));
|
||||
assertTrue(responseJson.contains("expires_in"));
|
||||
assertTrue(responseJson.contains("scope"));
|
||||
assertTrue(responseJson.contains("uid"));
|
||||
assertTrue(responseJson.contains("netEaseToken"));
|
||||
|
||||
// 验证结构与OAuth2兼容
|
||||
AuthResult authResult = objectMapper.readValue(responseJson, AuthResult.class);
|
||||
assertNotNull(authResult.getAccessToken());
|
||||
assertEquals("Bearer", authResult.getTokenType());
|
||||
assertNotNull(authResult.getUid());
|
||||
assertNotNull(authResult.getNetEaseToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试用户注销端点兼容性
|
||||
*/
|
||||
@Test
|
||||
public void testLogoutCompatibility() throws Exception {
|
||||
// 先获取token
|
||||
MvcResult loginResult = mockMvc.perform(post("/oauth/token")
|
||||
.param("grant_type", "password")
|
||||
.param("phone", "13800138000")
|
||||
.param("password", "123456")
|
||||
.param("client_id", "erban-client")
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn();
|
||||
|
||||
String responseJson = loginResult.getResponse().getContentAsString();
|
||||
AuthResult authResult = objectMapper.readValue(responseJson, AuthResult.class);
|
||||
|
||||
// 测试注销
|
||||
mockMvc.perform(post("/acc/logout")
|
||||
.param("access_token", authResult.getAccessToken())
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.message").value("success"));
|
||||
}
|
||||
}
|
21
accompany-oauth/pom.xml
Normal file
21
accompany-oauth/pom.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.accompany</groupId>
|
||||
<artifactId>accompany-dependencies</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../accompany-dependencies</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>accompany-oauth</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>accompany-oauth-sdk</module>
|
||||
<module>accompany-oauth-service</module>
|
||||
<module>accompany-oauth-web</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
@@ -3,7 +3,6 @@ package com.accompany.oauth2.jwt;
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
import com.accompany.core.service.common.JedisService;
|
||||
import com.accompany.oauth2.model.AccountDetails;
|
||||
import com.accompany.oauth2.service.account.AccountH5LoginService;
|
||||
import com.accompany.oauth2.token.CustomOAuth2AccessToken;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -18,9 +17,6 @@ public class JwtTokenConverter extends JwtAccessTokenConverter {
|
||||
@Autowired
|
||||
private JedisService jedisService;
|
||||
|
||||
@Autowired
|
||||
private AccountH5LoginService accountH5LoginService;
|
||||
|
||||
@Override
|
||||
public CustomOAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
|
||||
accessToken = super.enhance(accessToken, authentication);
|
||||
|
@@ -1,99 +0,0 @@
|
||||
package com.accompany.oauth2.service.account;
|
||||
|
||||
import com.accompany.common.redis.RedisKey;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import com.accompany.core.base.SpringContextHolder;
|
||||
import com.accompany.core.exception.ServiceException;
|
||||
import com.accompany.core.model.Users;
|
||||
import com.accompany.core.mybatismapper.AccountLoginRecordMapperExpand;
|
||||
import com.accompany.core.service.common.JedisService;
|
||||
import com.accompany.core.service.user.UsersBaseService;
|
||||
import com.accompany.core.util.JwtUtils;
|
||||
import com.accompany.oauth2.support.h5.H5TokenGranter;
|
||||
import com.accompany.oauth2.token.H5AccessToken;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by yuanyi on 2019/2/21.
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class AccountH5LoginService {
|
||||
|
||||
private static final long H5_JWT_TOKEN_EX = 60 * 60 * 1000 * 2L;
|
||||
|
||||
@Autowired
|
||||
private UsersBaseService usersBaseService;
|
||||
@Autowired
|
||||
private AccountLoginRecordMapperExpand accountLoginRecordMapperExpand;
|
||||
@Autowired
|
||||
private JedisService jedisService;
|
||||
@Autowired
|
||||
private JwtUtils jwtUtils;
|
||||
|
||||
public String createJwtToken(Long uid) {
|
||||
return jwtUtils.createJWT(H5_JWT_TOKEN_EX, uid);
|
||||
}
|
||||
|
||||
private void saveH5LoginJwtToken(Long uid, String jwtToken) {
|
||||
jedisService.hset(RedisKey.h5loginjwtoken.getKey(), uid.toString(), jwtToken);
|
||||
}
|
||||
|
||||
private void deleteH5LoginJwtToken(Long uid) {
|
||||
jedisService.hdel(RedisKey.h5loginjwtoken.getKey(), uid.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建h5令牌
|
||||
*
|
||||
* @param uid
|
||||
* @return
|
||||
*/
|
||||
public H5AccessToken createH5AccessToken(Long uid) {
|
||||
//限制只是华语区
|
||||
Users u = usersBaseService.getUsersByUid(uid);
|
||||
if (null == u){
|
||||
throw new ServiceException(BusiStatus.USERNOTEXISTS);
|
||||
}
|
||||
// PartitionUtil.checkInPartition(4, u.getPartitionId());
|
||||
|
||||
String jwtToken = createJwtToken(uid);
|
||||
saveH5LoginJwtToken(uid, jwtToken);
|
||||
H5AccessToken accessToken = new H5AccessToken();
|
||||
accessToken.setAccess_token(jwtToken);
|
||||
accessToken.setUid(uid);
|
||||
accessToken.setExpires_in(H5_JWT_TOKEN_EX);
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取token
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public H5AccessToken token(ServletWebRequest request) {
|
||||
String grantType = request.getParameter("grant_type");
|
||||
Map<String, H5TokenGranter> tokenGranterMap = SpringContextHolder.getApplicationContext().getBeansOfType(H5TokenGranter.class);
|
||||
for (H5TokenGranter tokenGranter : tokenGranterMap.values()) {
|
||||
if (tokenGranter.getGrantType().equals(grantType)) {
|
||||
H5AccessToken token = tokenGranter.getAuthentication(request);
|
||||
if (token != null) {
|
||||
Long uid = token.getUid();
|
||||
Integer isExists = accountLoginRecordMapperExpand.isExists(uid);
|
||||
if (isExists > 0) {
|
||||
deleteH5LoginJwtToken(uid);
|
||||
throw new ServiceException(BusiStatus.REGION_NOT_OPEN_UP);
|
||||
}
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -1,47 +0,0 @@
|
||||
package com.accompany.oauth2.support.h5;
|
||||
|
||||
import com.accompany.oauth2.token.H5AccessToken;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author: liaozetao
|
||||
* @date: 2023/7/17 10:25
|
||||
* @description:
|
||||
*/
|
||||
public abstract class AbstractH5TokenGranter implements H5TokenGranter {
|
||||
|
||||
protected static final String PHONE_AREA_CODE = "phoneAreaCode";
|
||||
|
||||
protected static final String PHONE = "phone";
|
||||
|
||||
protected static final String PASSWORD = "password";
|
||||
|
||||
protected static final String CODE = "code";
|
||||
|
||||
private final String grantType;
|
||||
|
||||
public AbstractH5TokenGranter(String grantType) {
|
||||
this.grantType = grantType;
|
||||
}
|
||||
|
||||
public H5AccessToken getAuthentication(ServletWebRequest request) {
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String[] value = entry.getValue();
|
||||
if (value.length > 0) {
|
||||
parameters.put(key, value[0]);
|
||||
}
|
||||
}
|
||||
return authenticate(parameters);
|
||||
}
|
||||
|
||||
public abstract H5AccessToken authenticate(Map<String, Object> parameters);
|
||||
|
||||
public String getGrantType() {
|
||||
return grantType;
|
||||
}
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
package com.accompany.oauth2.support.h5;
|
||||
|
||||
import com.accompany.oauth2.token.H5AccessToken;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
/**
|
||||
* @author: liaozetao
|
||||
* @date: 2023/7/17 12:16
|
||||
* @description:
|
||||
*/
|
||||
public interface H5TokenGranter {
|
||||
|
||||
/**
|
||||
* 获取令牌
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
H5AccessToken getAuthentication(ServletWebRequest request);
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getGrantType();
|
||||
}
|
@@ -1,71 +0,0 @@
|
||||
package com.accompany.oauth2.support.h5;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.accompany.common.device.DeviceInfo;
|
||||
import com.accompany.common.utils.DESUtils;
|
||||
import com.accompany.core.util.KeyStore;
|
||||
import com.accompany.core.util.MD5;
|
||||
import com.accompany.oauth2.constant.GrantTypeEnum;
|
||||
import com.accompany.oauth2.constant.LoginTypeEnum;
|
||||
import com.accompany.oauth2.model.AccountDetails;
|
||||
import com.accompany.oauth2.service.MyUserDetailsService;
|
||||
import com.accompany.oauth2.service.account.AccountH5LoginService;
|
||||
import com.accompany.oauth2.token.H5AccessToken;
|
||||
import com.accompany.oauth2.util.RequestContextHolderUtils;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.beanutils.BeanUtils;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author: liaozetao
|
||||
* @date: 2023/7/17 10:37
|
||||
* @description:
|
||||
*/
|
||||
@Slf4j
|
||||
public class PasswordH5TokenGranter extends AbstractH5TokenGranter {
|
||||
|
||||
private final MyUserDetailsService userDetailsService;
|
||||
|
||||
private final AccountH5LoginService accountH5LoginService;
|
||||
|
||||
public PasswordH5TokenGranter(MyUserDetailsService userDetailsService, AccountH5LoginService accountH5LoginService) {
|
||||
super(GrantTypeEnum.PASSWORD.getValue());
|
||||
this.userDetailsService = userDetailsService;
|
||||
this.accountH5LoginService = accountH5LoginService;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public H5AccessToken authenticate(Map<String, Object> parameters) {
|
||||
String phoneAreaCode = StrUtil.toString(parameters.get(PHONE_AREA_CODE));
|
||||
String username = StrUtil.toString(parameters.get(PHONE));
|
||||
String password = StrUtil.toString(parameters.get(PASSWORD));
|
||||
String code = StrUtil.toString(parameters.get(CODE));
|
||||
String ipAddress = RequestContextHolderUtils.getRemoteAddr();
|
||||
DeviceInfo deviceInfo = new DeviceInfo();
|
||||
try {
|
||||
BeanUtils.populate(deviceInfo, parameters);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
UserDetails userDetails;
|
||||
try {
|
||||
username = DESUtils.DESAndBase64Decrypt(username, KeyStore.DES_ENCRYPT_KEY);
|
||||
userDetails = userDetailsService.loadUserByPhone(username, phoneAreaCode, code, deviceInfo, ipAddress);
|
||||
try {
|
||||
password = MD5.getMD5(DESUtils.DESAndBase64Decrypt(password, KeyStore.DES_ENCRYPT_KEY));
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("密码非法");
|
||||
}
|
||||
userDetailsService.handlePwdLogin(username, password, userDetails);
|
||||
userDetailsService.login(username, userDetails, LoginTypeEnum.PASSWORD, deviceInfo, code);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
return accountH5LoginService.createH5AccessToken(((AccountDetails) userDetails).getAccount().getUid());
|
||||
}
|
||||
}
|
@@ -1,70 +0,0 @@
|
||||
package com.accompany.oauth2.support.h5;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.accompany.common.device.DeviceInfo;
|
||||
import com.accompany.common.status.BusiStatus;
|
||||
import com.accompany.core.service.user.PhoneBlackService;
|
||||
import com.accompany.oauth2.constant.GrantTypeEnum;
|
||||
import com.accompany.oauth2.constant.LoginTypeEnum;
|
||||
import com.accompany.oauth2.exception.CustomOAuth2Exception;
|
||||
import com.accompany.oauth2.model.AccountDetails;
|
||||
import com.accompany.oauth2.service.MyUserDetailsService;
|
||||
import com.accompany.oauth2.service.account.AccountH5LoginService;
|
||||
import com.accompany.oauth2.token.H5AccessToken;
|
||||
import com.accompany.oauth2.util.RequestContextHolderUtils;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.beanutils.BeanUtils;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author: liaozetao
|
||||
* @date: 2023/7/17 10:38
|
||||
* @description:
|
||||
*/
|
||||
@Slf4j
|
||||
public class VerifyCodeH5TokenGranter extends AbstractH5TokenGranter {
|
||||
|
||||
private final MyUserDetailsService userDetailsService;
|
||||
|
||||
private final PhoneBlackService phoneBlackService;
|
||||
|
||||
private final AccountH5LoginService accountH5LoginService;
|
||||
|
||||
public VerifyCodeH5TokenGranter(MyUserDetailsService userDetailsService, PhoneBlackService phoneBlackService, AccountH5LoginService accountH5LoginService) {
|
||||
super(GrantTypeEnum.VERIFY_CODE.getValue());
|
||||
this.userDetailsService = userDetailsService;
|
||||
this.phoneBlackService = phoneBlackService;
|
||||
this.accountH5LoginService = accountH5LoginService;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public H5AccessToken authenticate(Map<String, Object> parameters) {
|
||||
String phoneAreaCode = StrUtil.toString(parameters.get(PHONE_AREA_CODE));
|
||||
String phone = StrUtil.toString(parameters.get(PHONE));
|
||||
String code = StrUtil.toString(parameters.get(CODE));
|
||||
DeviceInfo deviceInfo = new DeviceInfo();
|
||||
try {
|
||||
BeanUtils.populate(deviceInfo, parameters);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
if (phoneBlackService.checkIsNeedIntercept(phone)) {
|
||||
throw new CustomOAuth2Exception(CustomOAuth2Exception.PHONE_BE_INTERCEPTED, BusiStatus.PHONE_BE_INTERCEPTED.getReasonPhrase());
|
||||
}
|
||||
UserDetails userDetails = null;
|
||||
try {
|
||||
userDetails = userDetailsService.loadUserByPhone(phone, phoneAreaCode, code, deviceInfo, RequestContextHolderUtils.getRemoteAddr());
|
||||
userDetailsService.login(phone, userDetails, LoginTypeEnum.ID, deviceInfo, code);
|
||||
} catch (CustomOAuth2Exception e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
return accountH5LoginService.createH5AccessToken(((AccountDetails) userDetails).getAccount().getUid());
|
||||
}
|
||||
}
|
@@ -4,11 +4,7 @@ import com.accompany.core.service.SysConfService;
|
||||
import com.accompany.core.service.user.PhoneBlackService;
|
||||
import com.accompany.oauth2.service.MyUserDetailsService;
|
||||
import com.accompany.oauth2.service.MyUserDetailsServiceImpl;
|
||||
import com.accompany.oauth2.service.account.AccountH5LoginService;
|
||||
import com.accompany.oauth2.support.email.EmailAuthenticationProvider;
|
||||
import com.accompany.oauth2.support.h5.H5TokenGranter;
|
||||
import com.accompany.oauth2.support.h5.PasswordH5TokenGranter;
|
||||
import com.accompany.oauth2.support.h5.VerifyCodeH5TokenGranter;
|
||||
import com.accompany.oauth2.support.password.PasswordAuthenticationProvider;
|
||||
import com.accompany.oauth2.support.verify.VerifyCodeAuthenticationProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -34,9 +30,6 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Autowired
|
||||
private PhoneBlackService phoneBlackService;
|
||||
|
||||
@Autowired
|
||||
private AccountH5LoginService accountH5LoginService;
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
protected UserDetailsService userDetailsService() {
|
||||
@@ -90,13 +83,4 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
return new EmailAuthenticationProvider(myUserDetailsService());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public H5TokenGranter passwordH5TokenGranter() {
|
||||
return new PasswordH5TokenGranter(myUserDetailsService(), accountH5LoginService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public H5TokenGranter verifyCodeH5TokenGranter() {
|
||||
return new VerifyCodeH5TokenGranter(myUserDetailsService(), phoneBlackService, accountH5LoginService);
|
||||
}
|
||||
}
|
||||
|
@@ -1,47 +0,0 @@
|
||||
package com.accompany.oauth2.controller;
|
||||
|
||||
import com.accompany.common.result.BusiResult;
|
||||
import com.accompany.core.exception.ServiceException;
|
||||
import com.accompany.oauth2.common.BaseController;
|
||||
import com.accompany.oauth2.service.account.AccountH5LoginService;
|
||||
import com.accompany.oauth2.token.H5AccessToken;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Created by yuanyi on 2019/2/22.
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/oauth/h5")
|
||||
public class H5LoginController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private AccountH5LoginService accountH5LoginService;
|
||||
|
||||
/**
|
||||
* 授权登录
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@ApiOperation("授权登录")
|
||||
@PostMapping("/token")
|
||||
public BusiResult<H5AccessToken> token(HttpServletRequest request, HttpServletResponse response) {
|
||||
try {
|
||||
return BusiResult.success(accountH5LoginService.token(new ServletWebRequest(request, response)));
|
||||
} catch (ServiceException e) {
|
||||
return BusiResult.fail(e.getBusiStatus(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user