oauth-重写-引入jjwt和删除h5登录相关

This commit is contained in:
2025-09-20 15:12:52 +08:00
parent 69dde07fc9
commit a762e35456
53 changed files with 1526 additions and 1819 deletions

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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 {
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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() + "神帽子");
});
}
}

View File

@@ -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;

View File

@@ -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));
}
}

View File

@@ -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());
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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);

View File

@@ -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();

View File

@@ -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);

View File

@@ -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;
}
/**
* 返回重新登录提示内容

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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 +
'}';
}

View File

@@ -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 + '\'' +
'}';
}
}

View File

@@ -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 + '\'' +
'}';
}
}

View File

@@ -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();
}

View File

@@ -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 序列化/反序列化(选一个)-->
<!-- 如果你项目用 JacksonSpring Boot 默认),选这个 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>${jjwt.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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());
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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: 实现登录记录保存逻辑
// 这里需要根据具体的业务需求来实现
}
}

View File

@@ -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);
}

View File

@@ -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("-", "");
}
}

View File

@@ -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();
}
}

View File

@@ -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("密码重置功能暂未实现");
}

View File

@@ -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();
}
}

View File

@@ -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>

View File

@@ -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());
}
}
}

View File

@@ -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
View 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>

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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());
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}
}