Merge branch 'dev/dev_xp' into dev/dev
This commit is contained in:
@@ -59,4 +59,10 @@ public class CommonConstant {
|
||||
|
||||
public static final List<String> IS_SUBSCRIBE = Arrays.asList("yes", "no");
|
||||
|
||||
public static final String DEFAULT_AVATAR = "aida-users/87/avatar/default.jpg";
|
||||
|
||||
/* 截止至2024/08/26,在Code-Create DB中pmr_users表中最大的用户id */
|
||||
public static final Long MAXIMUM_USER_ID = 704L;
|
||||
// public static final Long MAXIMUM_USER_ID = 225L;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,133 +1,134 @@
|
||||
package com.ai.da.common.security.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ai.da.common.context.UserContext;
|
||||
import com.ai.da.common.security.config.SecurityProperties;
|
||||
import com.ai.da.common.security.jwt.JWTTokenHelper;
|
||||
import com.ai.da.common.utils.LocalCacheUtils;
|
||||
import com.ai.da.common.utils.MultiReadHttpServletRequest;
|
||||
import com.ai.da.common.utils.MultiReadHttpServletResponse;
|
||||
import com.ai.da.common.utils.RequestInfoUtil;
|
||||
import com.ai.da.model.vo.AuthPrincipalVo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.util.StopWatch;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.security.sasl.AuthenticationException;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: 认证拦截器
|
||||
* @create: 2020-07-10 16:50
|
||||
**/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class AuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
@Resource
|
||||
private JWTTokenHelper jwtTokenHelper;
|
||||
@Resource
|
||||
private SecurityProperties properties;
|
||||
|
||||
private static final List<String> FILTER_URL =
|
||||
Arrays.asList("/favicon.ico", "/doc.html", "api/account/login", "api/account/preLogin", "api/account/sendEmail","api/account/noLoginRequired",
|
||||
"/webjars/", "/swagger-resources", "/v2/api-docs", "api/account/resetPwd",
|
||||
"/api/python/saveGeneratePicture", "/api/python/getLibraryByUserId",
|
||||
"/api/third/party/addUser","/api/third/party/addTrialUser", "/api/third/party/editUser", "/api/element/initDefaultSysFile",
|
||||
"/api/third/party/addNoLoginRequiredNew","/api/third/party/deleteNoLoginRequiredNew",
|
||||
"/api/third/party/existNoLoginRequired","/api/third/party/getRedirectUrl",
|
||||
"/api/python/flush","/api/account/healthy","/api/ali-pay/trade/notify","/api/paypal/ipn/back","/api/alipay-hk/trade/notify",
|
||||
"/api/portfolio/page", "/api/portfolio/detail", "/api/portfolio/commentPage", "/api/portfolio/viewsIncrease",
|
||||
"/api/account/designWorksRegister","/api/account/questionnaire","/api/stripe/trade/notify"
|
||||
);
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest httpServletRequest, @NonNull HttpServletResponse httpServletResponse, @NonNull FilterChain filterChain) throws ServletException, IOException {
|
||||
String requestURI = httpServletRequest.getRequestURI();
|
||||
|
||||
if (calculateUrl(requestURI) || hasAuthorizationToken(httpServletRequest)) {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
HttpServletRequest wrappedRequest = httpServletRequest;
|
||||
HttpServletResponse wrappedResponse = httpServletResponse;
|
||||
try {
|
||||
stopWatch.start();
|
||||
if ((httpServletRequest.getContentType() == null && httpServletRequest.getContentLength() > 0) || (httpServletRequest.getContentType() != null && !httpServletRequest.getContentType().contains("application/json"))) {
|
||||
extracted(wrappedRequest);
|
||||
filterChain.doFilter(wrappedRequest, wrappedResponse);
|
||||
} else {
|
||||
wrappedRequest = new MultiReadHttpServletRequest(httpServletRequest);
|
||||
wrappedResponse = new MultiReadHttpServletResponse(httpServletResponse);
|
||||
extracted(wrappedRequest);
|
||||
filterChain.doFilter(wrappedRequest, wrappedResponse);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
SecurityContextHolder.clearContext();
|
||||
throw e;
|
||||
} finally {
|
||||
stopWatch.stop();
|
||||
}
|
||||
} else {
|
||||
filterChain.doFilter(httpServletRequest, httpServletResponse);
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean calculateUrl(String requestURI) {
|
||||
String filterUrl = FILTER_URL.stream().filter(url -> requestURI.contains(url)).findFirst().orElse(null);
|
||||
return null == filterUrl ? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
|
||||
private boolean hasAuthorizationToken(HttpServletRequest request) {
|
||||
String authorizationHeader = request.getHeader("Authorization");
|
||||
return authorizationHeader != null && authorizationHeader.startsWith("Bearer");
|
||||
}
|
||||
|
||||
private void extracted(HttpServletRequest request) throws AuthenticationException {
|
||||
String jwtToken = request.getHeader(properties.getJwtTokenHeader());
|
||||
// log.debug("后台检查令牌:{}", jwtToken);
|
||||
|
||||
if (StrUtil.isBlank(jwtToken)) {
|
||||
String ipAddress = RequestInfoUtil.getIpAddress(request);
|
||||
log.info("本次请求的ip为 : " + ipAddress);
|
||||
throw new RuntimeException("请传入token!");
|
||||
}
|
||||
if(jwtToken.equals("Bearer-eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiIyIiwic3ViIjoie1wiaWRcIjoyLFwidXNlcm5hbWVcIjpcImxpcnNcIn0iLCJpYXQiOjE2NjU3NDEwODcsImlzcyI6IkRXSiIsImF1dGhvcml0aWVzIjoiW10iLCJleHAiOjE2NzQzODEwODd9.ShM9R_NNFD7oo1OvxrEgg7PFeWinOuAKkuInUCMQupp66s64Hhv8tN0Wwr83nIN4rHPqtn95wmd4msWcvaFYJA")){
|
||||
//写死 暂时放行
|
||||
return;
|
||||
}
|
||||
// 检查token
|
||||
boolean validate = jwtTokenHelper.validateToken(jwtToken);
|
||||
if (validate) {
|
||||
AuthPrincipalVo principal = jwtTokenHelper.parserToUser(jwtToken);
|
||||
if (principal == null) {
|
||||
throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||
}
|
||||
//先清空当前线程变量,防止上一个线程遗留
|
||||
UserContext.delete();
|
||||
//存取用户信息到缓存
|
||||
UserContext.setUserHolder(principal);
|
||||
//校验token
|
||||
String cacheToken = LocalCacheUtils.getTokenCache(String.valueOf(principal.getId()));
|
||||
|
||||
if(StringUtils.isEmpty(cacheToken)){
|
||||
throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||
}
|
||||
if(!cacheToken.equals(jwtToken) ){
|
||||
throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||
}
|
||||
// UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(null, null);
|
||||
// SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.ai.da.common.security.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ai.da.common.context.UserContext;
|
||||
import com.ai.da.common.security.config.SecurityProperties;
|
||||
import com.ai.da.common.security.jwt.JWTTokenHelper;
|
||||
import com.ai.da.common.utils.LocalCacheUtils;
|
||||
import com.ai.da.common.utils.MultiReadHttpServletRequest;
|
||||
import com.ai.da.common.utils.MultiReadHttpServletResponse;
|
||||
import com.ai.da.common.utils.RequestInfoUtil;
|
||||
import com.ai.da.model.vo.AuthPrincipalVo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.util.StopWatch;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.security.sasl.AuthenticationException;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: 认证拦截器
|
||||
* @create: 2020-07-10 16:50
|
||||
**/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class AuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
@Resource
|
||||
private JWTTokenHelper jwtTokenHelper;
|
||||
@Resource
|
||||
private SecurityProperties properties;
|
||||
|
||||
private static final List<String> FILTER_URL =
|
||||
Arrays.asList("/favicon.ico", "/doc.html", "api/account/login", "api/account/preLogin", "api/account/sendEmail","api/account/noLoginRequired",
|
||||
"/webjars/", "/swagger-resources", "/v2/api-docs", "api/account/resetPwd",
|
||||
"/api/python/saveGeneratePicture", "/api/python/getLibraryByUserId",
|
||||
"/api/third/party/addUser","/api/third/party/addTrialUser", "/api/third/party/editUser", "/api/element/initDefaultSysFile",
|
||||
"/api/third/party/addNoLoginRequiredNew","/api/third/party/deleteNoLoginRequiredNew",
|
||||
"/api/third/party/existNoLoginRequired","/api/third/party/getRedirectUrl",
|
||||
"/api/python/flush","/api/account/healthy","/api/ali-pay/trade/notify","/api/paypal/ipn/back","/api/alipay-hk/trade/notify",
|
||||
"/api/portfolio/page", "/api/portfolio/detail", "/api/portfolio/commentPage", "/api/portfolio/viewsIncrease",
|
||||
"/api/account/designWorksRegister","/api/account/questionnaire","/api/stripe/trade/notify",
|
||||
"/notification"
|
||||
);
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest httpServletRequest, @NonNull HttpServletResponse httpServletResponse, @NonNull FilterChain filterChain) throws ServletException, IOException {
|
||||
String requestURI = httpServletRequest.getRequestURI();
|
||||
|
||||
if (calculateUrl(requestURI) || hasAuthorizationToken(httpServletRequest)) {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
HttpServletRequest wrappedRequest = httpServletRequest;
|
||||
HttpServletResponse wrappedResponse = httpServletResponse;
|
||||
try {
|
||||
stopWatch.start();
|
||||
if ((httpServletRequest.getContentType() == null && httpServletRequest.getContentLength() > 0) || (httpServletRequest.getContentType() != null && !httpServletRequest.getContentType().contains("application/json"))) {
|
||||
extracted(wrappedRequest);
|
||||
filterChain.doFilter(wrappedRequest, wrappedResponse);
|
||||
} else {
|
||||
wrappedRequest = new MultiReadHttpServletRequest(httpServletRequest);
|
||||
wrappedResponse = new MultiReadHttpServletResponse(httpServletResponse);
|
||||
extracted(wrappedRequest);
|
||||
filterChain.doFilter(wrappedRequest, wrappedResponse);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
SecurityContextHolder.clearContext();
|
||||
throw e;
|
||||
} finally {
|
||||
stopWatch.stop();
|
||||
}
|
||||
} else {
|
||||
filterChain.doFilter(httpServletRequest, httpServletResponse);
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean calculateUrl(String requestURI) {
|
||||
String filterUrl = FILTER_URL.stream().filter(url -> requestURI.contains(url)).findFirst().orElse(null);
|
||||
return null == filterUrl ? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
|
||||
private boolean hasAuthorizationToken(HttpServletRequest request) {
|
||||
String authorizationHeader = request.getHeader("Authorization");
|
||||
return authorizationHeader != null && authorizationHeader.startsWith("Bearer");
|
||||
}
|
||||
|
||||
private void extracted(HttpServletRequest request) throws AuthenticationException {
|
||||
String jwtToken = request.getHeader(properties.getJwtTokenHeader());
|
||||
// log.debug("后台检查令牌:{}", jwtToken);
|
||||
|
||||
if (StrUtil.isBlank(jwtToken)) {
|
||||
String ipAddress = RequestInfoUtil.getIpAddress(request);
|
||||
log.info("本次请求的ip为 : " + ipAddress);
|
||||
throw new RuntimeException("请传入token!");
|
||||
}
|
||||
if(jwtToken.equals("Bearer-eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiIyIiwic3ViIjoie1wiaWRcIjoyLFwidXNlcm5hbWVcIjpcImxpcnNcIn0iLCJpYXQiOjE2NjU3NDEwODcsImlzcyI6IkRXSiIsImF1dGhvcml0aWVzIjoiW10iLCJleHAiOjE2NzQzODEwODd9.ShM9R_NNFD7oo1OvxrEgg7PFeWinOuAKkuInUCMQupp66s64Hhv8tN0Wwr83nIN4rHPqtn95wmd4msWcvaFYJA")){
|
||||
//写死 暂时放行
|
||||
return;
|
||||
}
|
||||
// 检查token
|
||||
boolean validate = jwtTokenHelper.validateToken(jwtToken);
|
||||
if (validate) {
|
||||
AuthPrincipalVo principal = jwtTokenHelper.parserToUser(jwtToken);
|
||||
if (principal == null) {
|
||||
throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||
}
|
||||
//先清空当前线程变量,防止上一个线程遗留
|
||||
UserContext.delete();
|
||||
//存取用户信息到缓存
|
||||
UserContext.setUserHolder(principal);
|
||||
//校验token
|
||||
String cacheToken = LocalCacheUtils.getTokenCache(String.valueOf(principal.getId()));
|
||||
|
||||
if(StringUtils.isEmpty(cacheToken)){
|
||||
throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||
}
|
||||
if(!cacheToken.equals(jwtToken) ){
|
||||
throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||
}
|
||||
// UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(null, null);
|
||||
// SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,4 +57,12 @@ public class AccountTask {
|
||||
accountService.toVisitor(account);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Code-Create上注册的用户添加为AiDA的游客
|
||||
*/
|
||||
// @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||
public void registerUserToVisitor(){
|
||||
accountService.registerUserToVisitor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,4 +216,17 @@ public class RedisUtil {
|
||||
return redisTemplate.opsForValue().increment(key, 0);
|
||||
}
|
||||
|
||||
public final static String PERSONAL_HOMEPAGE_VIEW_KEY = "PersonalHomepage:view:";
|
||||
|
||||
public void increasePersonalHomepageViewCount(Long accountId) {
|
||||
String key = PERSONAL_HOMEPAGE_VIEW_KEY + accountId;
|
||||
redisTemplate.opsForValue().increment(key);
|
||||
}
|
||||
|
||||
public Long getPersonalHomepageViewCount(Long accountId) {
|
||||
String key = PERSONAL_HOMEPAGE_VIEW_KEY + accountId;
|
||||
return redisTemplate.opsForValue().increment(key, 0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -590,4 +590,40 @@ public class SendEmailUtil {
|
||||
throw new BusinessException("failed.to.send.mail");
|
||||
}
|
||||
}
|
||||
|
||||
private final static Long NEW_USER_REGISTER_NOTIFICATION_EN = 126919L;
|
||||
|
||||
public static void notificationForRegisterUser(String receiverAddress){
|
||||
try{
|
||||
// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
|
||||
// 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305
|
||||
// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
|
||||
Credential cred = new Credential(SECRET_ID, SECRET_KEy);
|
||||
// 实例化一个http选项,可选的,没有特殊需求可以跳过
|
||||
HttpProfile httpProfile = new HttpProfile();
|
||||
httpProfile.setEndpoint("ses.tencentcloudapi.com");
|
||||
// 实例化一个client选项,可选的,没有特殊需求可以跳过
|
||||
ClientProfile clientProfile = new ClientProfile();
|
||||
clientProfile.setHttpProfile(httpProfile);
|
||||
// 实例化要请求产品的client对象,clientProfile是可选的
|
||||
SesClient client = new SesClient(cred, "ap-hongkong", clientProfile);
|
||||
// 实例化一个请求对象,每个接口都会对应一个request对象
|
||||
SendEmailRequest req = new SendEmailRequest();
|
||||
req.setFromEmailAddress(SEND_ADDRESS);
|
||||
req.setDestination(new String[]{receiverAddress});
|
||||
|
||||
req.setSubject("Welcome to AiDa");
|
||||
Template template = new Template();
|
||||
template.setTemplateID(NEW_USER_REGISTER_NOTIFICATION_EN);
|
||||
template.setTemplateData("{\"email\":\" " + receiverAddress + "\"}");
|
||||
req.setTemplate(template);
|
||||
|
||||
// 返回的resp是一个SendEmailResponse的实例,与请求对象对应
|
||||
SendEmailResponse resp = client.SendEmail(req);
|
||||
log.info("短信发送结果res###{}", SendEmailResponse.toJsonString(resp));
|
||||
} catch (TencentCloudSDKException e) {
|
||||
log.info("邮件发送失败###{}", e.toString());
|
||||
throw new BusinessException("failed.to.send.mail");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.ai.da.common.websocket;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.websocket.*;
|
||||
import javax.websocket.server.ServerEndpoint;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
@ServerEndpoint(value = "/notification")
|
||||
@Component
|
||||
@Slf4j
|
||||
public class NotificationConnection {
|
||||
|
||||
static Map<String,Session> sessionMap = new ConcurrentHashMap<>();
|
||||
//连接建立时执行的操作
|
||||
@OnOpen
|
||||
public void onOpen(Session session){
|
||||
sessionMap.put(session.getId(),session);
|
||||
log.info("websocket is open");
|
||||
}
|
||||
//收到了客户端消息执行的操作
|
||||
@OnMessage
|
||||
public void onMessage(String text){
|
||||
log.info("收到了一条消息:"+text);
|
||||
// return "已收到你的消息";
|
||||
}
|
||||
//连接关闭的时候执行的操作
|
||||
@OnClose
|
||||
public void onClose(Session session){
|
||||
sessionMap.remove(session.getId());
|
||||
log.info("websocket is close");
|
||||
}
|
||||
|
||||
public void sendMsg(String message) throws IOException {
|
||||
for(String key:sessionMap.keySet()){
|
||||
sessionMap.get(key).getBasicRemote().sendText(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ai.da.common.websocket.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||
|
||||
/**
|
||||
* Configuration of WebSocket
|
||||
*
|
||||
* @author db1995
|
||||
*/
|
||||
@Configuration
|
||||
public class WebSocketConfig {
|
||||
@Bean
|
||||
public ServerEndpointExporter serverEndpointExporter() {
|
||||
return new ServerEndpointExporter();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user