提交
21
pxdj-java/pxdj-security/pom.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>pxdj-java</artifactId>
|
||||
<groupId>com.pxdj</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>pxdj-security</artifactId>
|
||||
|
||||
<description>商城安全模块</description>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>pxdj-security-api</module>
|
||||
<module>pxdj-security-common</module>
|
||||
</modules>
|
||||
</project>
|
||||
41
pxdj-java/pxdj-security/pxdj-security-api/pom.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?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">
|
||||
<parent>
|
||||
<groupId>com.pxdj</groupId>
|
||||
<artifactId>pxdj-security</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>pxdj-security-api</artifactId>
|
||||
<description>商城安全模块接口部分</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.pxdj</groupId>
|
||||
<artifactId>pxdj-security-common</artifactId>
|
||||
<version>${pxdj.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>4.9.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
<version>1.56</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.json-lib</groupId>
|
||||
<artifactId>json-lib</artifactId>
|
||||
<version>2.4</version>
|
||||
<classifier>jdk15</classifier>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.pxdj.security.api.adapter;
|
||||
|
||||
import com.pxdj.common.adapter.DefaultAuthConfigAdapter;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/28 15:17
|
||||
*/
|
||||
@Primary
|
||||
@Component("apiResourceServerAdapter")
|
||||
public class ResourceServerAdapter extends DefaultAuthConfigAdapter {
|
||||
|
||||
@Override
|
||||
public List<String> pathPatterns() {
|
||||
return Collections.singletonList("/p/*");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
package com.yami.shop.security.api.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.yami.shop.bean.model.User;
|
||||
import com.yami.shop.common.exception.YamiShopBindException;
|
||||
import com.yami.shop.common.util.PrincipalUtil;
|
||||
import com.yami.shop.dao.UserMapper;
|
||||
import com.yami.shop.security.common.bo.UserInfoInTokenBO;
|
||||
import com.yami.shop.security.common.dto.AuthenticationDTO;
|
||||
import com.yami.shop.security.common.enums.SysTypeEnum;
|
||||
import com.yami.shop.security.common.manager.PasswordCheckManager;
|
||||
import com.yami.shop.security.common.manager.PasswordManager;
|
||||
import com.yami.shop.security.common.manager.TokenStore;
|
||||
import com.yami.shop.security.common.vo.TokenInfoVO;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import com.yami.shop.common.response.ServerResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
*/
|
||||
/**
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/28 15:20
|
||||
*//*
|
||||
|
||||
@RestController
|
||||
@Tag(name = "登录")
|
||||
public class LoginController {
|
||||
@Autowired
|
||||
private TokenStore tokenStore;
|
||||
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Autowired
|
||||
private PasswordCheckManager passwordCheckManager;
|
||||
|
||||
@Autowired
|
||||
private PasswordManager passwordManager;
|
||||
|
||||
@PostMapping("/login")
|
||||
@Operation(summary = "账号密码(用于前端登录)" , description = "通过账号/手机号/用户名密码登录,还要携带用户的类型,也就是用户所在的系统")
|
||||
public ServerResponseEntity<TokenInfoVO> login(
|
||||
@Valid @RequestBody AuthenticationDTO authenticationDTO) {
|
||||
String mobileOrUserName = authenticationDTO.getUserName();
|
||||
User user = getUser(mobileOrUserName);
|
||||
String decryptPassword = passwordManager.decryptPassword(authenticationDTO.getPassWord());
|
||||
// 半小时内密码输入错误十次,已限制登录30分钟
|
||||
passwordCheckManager.checkPassword(SysTypeEnum.ORDINARY,authenticationDTO.getUserName(), decryptPassword, user.getLoginPassword());
|
||||
UserInfoInTokenBO userInfoInToken = new UserInfoInTokenBO();
|
||||
userInfoInToken.setUserId(user.getUserId());
|
||||
userInfoInToken.setSysType(SysTypeEnum.ORDINARY.value());
|
||||
userInfoInToken.setEnabled(user.getStatus() == 1);
|
||||
// 存储token返回vo
|
||||
TokenInfoVO tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken);
|
||||
return ServerResponseEntity.success(tokenInfoVO);
|
||||
}
|
||||
|
||||
private User getUser(String mobileOrUserName) {
|
||||
User user = null;
|
||||
// 手机验证码登陆,或传过来的账号很像手机号
|
||||
if (PrincipalUtil.isMobile(mobileOrUserName)) {
|
||||
user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserMobile, mobileOrUserName));
|
||||
}
|
||||
// 如果不是手机验证码登陆, 找不到手机号就找用户名
|
||||
if (user == null) {
|
||||
user = userMapper.selectOneByUserName(mobileOrUserName);
|
||||
}
|
||||
if (user == null) {
|
||||
throw new YamiShopBindException("账号或密码不正确");
|
||||
}
|
||||
return user;
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.pxdj.security.api.manager;
|
||||
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 异步任务管理器
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class AsyncManager
|
||||
{
|
||||
/**
|
||||
* 操作延迟10毫秒
|
||||
*/
|
||||
private final int OPERATE_DELAY_TIME = 10;
|
||||
|
||||
/**
|
||||
* 异步操作任务调度线程池
|
||||
*/
|
||||
private ScheduledExecutorService executor = SpringsUtils.getBean("scheduledExecutorService");
|
||||
|
||||
/**
|
||||
* 单例模式
|
||||
*/
|
||||
private AsyncManager(){}
|
||||
|
||||
private static AsyncManager me = new AsyncManager();
|
||||
|
||||
public static AsyncManager me()
|
||||
{
|
||||
return me;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
*
|
||||
* @param task 任务
|
||||
*/
|
||||
public void execute(TimerTask task)
|
||||
{
|
||||
executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止任务线程池
|
||||
*/
|
||||
public void shutdown()
|
||||
{
|
||||
Threads.shutdownAndAwaitTermination(executor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package com.pxdj.security.api.manager;
|
||||
|
||||
import org.springframework.aop.framework.AopContext;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* spring工具类 方便在非spring管理环境中获取bean
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
public final class SpringsUtils implements BeanFactoryPostProcessor, ApplicationContextAware
|
||||
{
|
||||
/** Spring应用上下文环境 */
|
||||
private static ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
|
||||
{
|
||||
SpringsUtils.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
|
||||
{
|
||||
SpringsUtils.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象
|
||||
*
|
||||
* @param name
|
||||
* @return Object 一个以所给名字注册的bean的实例
|
||||
* @throws org.springframework.beans.BeansException
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getBean(String name) throws BeansException
|
||||
{
|
||||
return (T) beanFactory.getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型为requiredType的对象
|
||||
*
|
||||
* @param clz
|
||||
* @return
|
||||
* @throws org.springframework.beans.BeansException
|
||||
*
|
||||
*/
|
||||
public static <T> T getBean(Class<T> clz) throws BeansException
|
||||
{
|
||||
T result = (T) beanFactory.getBean(clz);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
|
||||
*
|
||||
* @param name
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean containsBean(String name)
|
||||
{
|
||||
return beanFactory.containsBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
|
||||
*
|
||||
* @param name
|
||||
* @return boolean
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*
|
||||
*/
|
||||
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
|
||||
{
|
||||
return beanFactory.isSingleton(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return Class 注册对象的类型
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*
|
||||
*/
|
||||
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
|
||||
{
|
||||
return beanFactory.getType(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*
|
||||
*/
|
||||
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
|
||||
{
|
||||
return beanFactory.getAliases(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取aop代理对象
|
||||
*
|
||||
* @param invoker
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getAopProxy(T invoker)
|
||||
{
|
||||
return (T) AopContext.currentProxy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的环境配置,无配置返回null
|
||||
*
|
||||
* @return 当前的环境配置
|
||||
*/
|
||||
public static String[] getActiveProfiles()
|
||||
{
|
||||
return applicationContext.getEnvironment().getActiveProfiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的环境配置,当有多个环境配置时,只获取第一个
|
||||
*
|
||||
* @return 当前的环境配置
|
||||
*/
|
||||
public static String getActiveProfile()
|
||||
{
|
||||
final String[] activeProfiles = getActiveProfiles();
|
||||
return Optional.ofNullable(activeProfiles)
|
||||
.filter(profiles -> profiles.length > 0)
|
||||
.map(profiles -> profiles[0])
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置文件中的值
|
||||
*
|
||||
* @param key 配置文件的key
|
||||
* @return 当前的配置文件的值
|
||||
*
|
||||
*/
|
||||
public static String getRequiredProperty(String key)
|
||||
{
|
||||
return applicationContext.getEnvironment().getRequiredProperty(key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.pxdj.security.api.manager;
|
||||
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 线程相关工具类.
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class Threads
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(Threads.class);
|
||||
|
||||
/**
|
||||
* sleep等待,单位为毫秒
|
||||
*/
|
||||
public static void sleep(long milliseconds)
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(milliseconds);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止线程池
|
||||
* 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
|
||||
* 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数.
|
||||
* 如果仍然超時,則強制退出.
|
||||
* 另对在shutdown时线程本身被调用中断做了处理.
|
||||
*/
|
||||
public static void shutdownAndAwaitTermination(ExecutorService pool)
|
||||
{
|
||||
if (pool != null && !pool.isShutdown())
|
||||
{
|
||||
pool.shutdown();
|
||||
try
|
||||
{
|
||||
if (!pool.awaitTermination(120, TimeUnit.SECONDS))
|
||||
{
|
||||
pool.shutdownNow();
|
||||
if (!pool.awaitTermination(120, TimeUnit.SECONDS))
|
||||
{
|
||||
logger.info("Pool did not terminate");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedException ie)
|
||||
{
|
||||
pool.shutdownNow();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印线程异常信息
|
||||
*/
|
||||
public static void printException(Runnable r, Throwable t)
|
||||
{
|
||||
if (t == null && r instanceof Future<?>)
|
||||
{
|
||||
try
|
||||
{
|
||||
Future<?> future = (Future<?>) r;
|
||||
if (future.isDone())
|
||||
{
|
||||
future.get();
|
||||
}
|
||||
}
|
||||
catch (CancellationException ce)
|
||||
{
|
||||
t = ce;
|
||||
}
|
||||
catch (ExecutionException ee)
|
||||
{
|
||||
t = ee.getCause();
|
||||
}
|
||||
catch (InterruptedException ie)
|
||||
{
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
if (t != null)
|
||||
{
|
||||
logger.error(t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.pxdj.security.api.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class Phone {
|
||||
private String encryptedData;
|
||||
private String iv;
|
||||
private String code;
|
||||
private String openid;
|
||||
private String sessionKey;
|
||||
private String inviteCode;
|
||||
private String sessionId;
|
||||
private String unionid;
|
||||
|
||||
//小程序登陆时返回的code(使用code登陆必填)
|
||||
private String openIdCode;
|
||||
private String phoneCode;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.pxdj.security.api.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class WxInfo {
|
||||
private String session_key;
|
||||
private String openid;
|
||||
private String unionid;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.security.api.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 用户详细信息
|
||||
* @author LGH
|
||||
*/
|
||||
@Data
|
||||
public class YamiUser {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
private String bizUserId;
|
||||
|
||||
private Boolean enabled;
|
||||
|
||||
/**
|
||||
* 自提点Id
|
||||
*/
|
||||
private Long stationId;
|
||||
|
||||
/**
|
||||
* 店铺Id
|
||||
*/
|
||||
private Long shopId;
|
||||
|
||||
/**
|
||||
* 微信opendid
|
||||
*/
|
||||
private String openid;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.security.api.util;
|
||||
|
||||
import com.pxdj.security.api.model.YamiUser;
|
||||
import com.pxdj.common.bo.UserInfoInTokenBO;
|
||||
import com.pxdj.common.util.AuthUserContext;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
/**
|
||||
* @author LGH
|
||||
*/
|
||||
@UtilityClass
|
||||
public class SecurityUtils {
|
||||
|
||||
private static final String USER_REQUEST = "/p/";
|
||||
|
||||
/**
|
||||
* 获取用户
|
||||
*/
|
||||
public YamiUser getUser() {
|
||||
/*if (!HttpContextUtils.getHttpServletRequest().getRequestURI().startsWith(USER_REQUEST)) {
|
||||
// 用户相关的请求,应该以/p开头!!!
|
||||
throw new RuntimeException("yami.user.request.error");
|
||||
}*/
|
||||
UserInfoInTokenBO userInfoInTokenBO = AuthUserContext.get();
|
||||
|
||||
YamiUser yamiUser = new YamiUser();
|
||||
yamiUser.setUserId(userInfoInTokenBO.getUserId());
|
||||
yamiUser.setBizUserId(userInfoInTokenBO.getBizUserId());
|
||||
yamiUser.setEnabled(userInfoInTokenBO.getEnabled());
|
||||
yamiUser.setShopId(userInfoInTokenBO.getShopId());
|
||||
yamiUser.setStationId(userInfoInTokenBO.getOtherId());
|
||||
yamiUser.setOpenid(userInfoInTokenBO.getOpenid());
|
||||
return yamiUser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.pxdj.security.api.wx;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.*;
|
||||
|
||||
/**
|
||||
* AES加密
|
||||
* @author liuyazhuang
|
||||
*
|
||||
*/
|
||||
public class AES {
|
||||
|
||||
public static boolean initialized = false;
|
||||
|
||||
/**
|
||||
* AES解密
|
||||
*
|
||||
* @param content
|
||||
* 密文
|
||||
* @return
|
||||
* @throws InvalidAlgorithmParameterException
|
||||
* @throws NoSuchProviderException
|
||||
*/
|
||||
public byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) throws InvalidAlgorithmParameterException {
|
||||
initialize();
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
|
||||
Key sKeySpec = new SecretKeySpec(keyByte, "AES");
|
||||
cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化
|
||||
byte[] result = cipher.doFinal(content);
|
||||
return result;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchPaddingException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidKeyException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
e.printStackTrace();
|
||||
} catch (BadPaddingException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchProviderException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
if (initialized)
|
||||
return;
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
// 生成iv
|
||||
public static AlgorithmParameters generateIV(byte[] iv) throws Exception {
|
||||
AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
|
||||
params.init(new IvParameterSpec(iv));
|
||||
return params;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,412 @@
|
||||
package com.pxdj.security.api.wx;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.pxdj.api.domain.User;
|
||||
import com.pxdj.api.service.UserService;
|
||||
import com.pxdj.common.bo.UserInfoInTokenBO;
|
||||
import com.pxdj.common.enums.SysTypeEnum;
|
||||
import com.pxdj.common.manager.PasswordManager;
|
||||
import com.pxdj.common.manager.TokenStore;
|
||||
import com.pxdj.common.response.ServerResponseEntity;
|
||||
import com.pxdj.common.utils.RedisUtil;
|
||||
import com.pxdj.common.utils.WeiXinService;
|
||||
import com.pxdj.common.utils.WeiXinUtil;
|
||||
import com.pxdj.common.vo.TokenInfoVO;
|
||||
import com.pxdj.security.api.model.Phone;
|
||||
import com.pxdj.security.api.model.WxInfo;
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.sf.json.JSONObject;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
/*import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.binarywang.wx.miniapp.util.WxMaConfigHolder;*/
|
||||
import static com.pxdj.common.utils.SpringContextUtils.applicationContext;
|
||||
//import static com.yami.shop.common.util.SpringContextUtils.applicationContext;
|
||||
|
||||
|
||||
/**
|
||||
* 封装对外访问方法
|
||||
*
|
||||
* @author liuyazhuang
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/wxcore")
|
||||
@AllArgsConstructor
|
||||
public class WXCore {
|
||||
private static final Logger log = LoggerFactory.getLogger(WXCore.class);
|
||||
|
||||
@Autowired
|
||||
private TokenStore tokenStore;
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
private final PasswordManager passwordManager;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private static final String WATERMARK = "watermark";
|
||||
private static final String APPID = "appid";
|
||||
@Autowired
|
||||
private WeiXinService weiXinService;
|
||||
@Autowired
|
||||
private WeiXinUtil weiXinUtil;
|
||||
|
||||
public static void printAaa() {
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟登录
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("rLogin")
|
||||
public ServerResponseEntity<TokenInfoVO> testLogin(@RequestBody User user) {
|
||||
User one = userService.lambdaQuery().eq(User::getUserMobile, user.getUserMobile())
|
||||
.eq(User::getUserType, 1).one();
|
||||
if (one == null) {
|
||||
return ServerResponseEntity.showFailMsg("手机号不存在");
|
||||
}
|
||||
|
||||
if (user.getSessionId() != null && !user.getSessionId().isEmpty()) {
|
||||
try {
|
||||
String distanceKey = "user_merchant_distance:" + user.getSessionId();
|
||||
Map<String, String> distanceMap = RedisUtil.get(distanceKey);
|
||||
if (distanceMap != null) {
|
||||
String newDistanceKey = "user_merchant_distance:" + user.getUserId();
|
||||
RedisUtil.set(newDistanceKey, distanceMap);
|
||||
// RedisUtil.del(locationKey);
|
||||
// RedisUtil.del(distanceKey);
|
||||
|
||||
log.info("用户登录成功,缓存数据从sessionId[{}]迁移到userId[{}]",
|
||||
user.getSessionId(), user.getUserId());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("迁移位置缓存数据失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
UserInfoInTokenBO userInfoInToken = new UserInfoInTokenBO();
|
||||
userInfoInToken.setUserId(one.getUserId());
|
||||
userInfoInToken.setSysType(SysTypeEnum.ORDINARY.value());
|
||||
userInfoInToken.setEnabled(true);
|
||||
// 存储token返回vo
|
||||
TokenInfoVO tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken);
|
||||
return ServerResponseEntity.success(tokenInfoVO);
|
||||
}
|
||||
|
||||
@PostMapping("merchantLogin")
|
||||
public ServerResponseEntity<TokenInfoVO> testtMerchantLogin(@RequestBody User user) {
|
||||
User one = userService.lambdaQuery().eq(User::getUserMobile, user.getUserMobile())
|
||||
.eq(User::getUserType, 2).one();
|
||||
if (one == null) {
|
||||
return ServerResponseEntity.showFailMsg("手机号不存在");
|
||||
}
|
||||
UserInfoInTokenBO userInfoInToken = new UserInfoInTokenBO();
|
||||
userInfoInToken.setUserId(one.getUserId());
|
||||
userInfoInToken.setSysType(SysTypeEnum.ORDINARY.value());
|
||||
userInfoInToken.setEnabled(true);
|
||||
// 存储token返回vo
|
||||
TokenInfoVO tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken);
|
||||
return ServerResponseEntity.success(tokenInfoVO);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*//**
|
||||
* 小程序授权登录
|
||||
*
|
||||
* @param loginParam
|
||||
* @return
|
||||
*//*
|
||||
@PostMapping("/login")
|
||||
@SneakyThrows
|
||||
public ServerResponseEntity<TokenInfoBO> login(@RequestBody LoginParam loginParam) {
|
||||
//根据appId获取登录类型
|
||||
AppIdEnum appIdEnum = AppIdEnum.getByAppId(loginParam.getAppId());
|
||||
Assert.isFalse(appIdEnum == null, "appId有误");
|
||||
//根据传入的appId进行切换ma
|
||||
wxMaService.switchoverTo(loginParam.getAppId());
|
||||
//获取用户的openId
|
||||
WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(loginParam.getOpenIdCode());
|
||||
WxMaPhoneNumberInfo phoneNoInfo = wxMaService.getUserService().getPhoneNumber(loginParam.getPhoneCode());
|
||||
WxMaConfigHolder.remove();//清理ThreadLocal
|
||||
String openId = session.getUnionid();
|
||||
//根据openId 获取用户 --
|
||||
User user = userService.lambdaQuery().eq(User::getOpenid, openId).eq(User::getUserType, appIdEnum.getSys()).one();
|
||||
//商户登录 若用户为空则不允许登录
|
||||
Assert.isFalse(appIdEnum.getSys() == AppIdEnum.MERCHANT.getSys() && user == null, "请先入住商户,审核通过后再进行登录");
|
||||
if (user == null) {
|
||||
Date now = new Date();
|
||||
userService.save(new User()
|
||||
.setModifyTime(now)
|
||||
.setUnionid(session.getUnionid())
|
||||
.setOpenid(openId)
|
||||
.setUserId(snowflake.nextIdStr())
|
||||
.setUserRegtime(now)
|
||||
.setUserType(appIdEnum.getSys())
|
||||
.setStatus(YesOrNoEnum.YES.getCode())
|
||||
.setUserMobile(phoneNoInfo.getPhoneNumber())
|
||||
);
|
||||
}
|
||||
// 4. 登录成功
|
||||
return ServerResponseEntity.success(tokenStore.storeAndGetVo(new MiniAppUser().setUserId(user.getUserId()).setOpenid(openId).setUnionid(session.getUnionid()).setSys(appIdEnum.getSys())
|
||||
|
||||
));
|
||||
}*/
|
||||
|
||||
/**
|
||||
* 解密数据
|
||||
*
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@PostMapping("/wxLogin")
|
||||
@Transactional
|
||||
public ServerResponseEntity<TokenInfoVO> decrypt(@RequestBody Phone phone) {
|
||||
String result = "";
|
||||
Environment environment = applicationContext.getEnvironment();
|
||||
String appId = environment.getProperty("weixin.appid");
|
||||
String secret = environment.getProperty("weixin.secret");
|
||||
TokenInfoVO tokenInfoVO = new TokenInfoVO();
|
||||
WxInfo wxInfo = getWeChatOpenId(phone.getOpenIdCode());
|
||||
User user = userService.selectUserByOpenId(wxInfo.getOpenid());
|
||||
if (user == null) {
|
||||
String accessToken = weiXinUtil.postToken(appId, secret);
|
||||
String tel = weiXinService.getUserPhoneNumber(accessToken, phone.getPhoneCode());
|
||||
user = new User();
|
||||
String userId = IdUtil.simpleUUID();
|
||||
Date now = new Date();
|
||||
user.setModifyTime(now);
|
||||
user.setUserRegtime(now);
|
||||
user.setUserMobile(tel);
|
||||
user.setOpenid(wxInfo.getOpenid());
|
||||
user.setUnionid(wxInfo.getUnionid());
|
||||
user.setUserId(userId);
|
||||
user.setStatus(1);
|
||||
userService.save(user);
|
||||
} else {
|
||||
user.setUnionid(wxInfo.getUnionid());
|
||||
userService.updateById(user);
|
||||
}
|
||||
if (phone.getSessionId() != null && !phone.getSessionId().isEmpty()) {
|
||||
try {
|
||||
String distanceKey = "user_merchant_distance:" + phone.getSessionId();
|
||||
Map<String, String> distanceMap = RedisUtil.get(distanceKey);
|
||||
if (distanceMap != null) {
|
||||
String newDistanceKey = "user_merchant_distance:" + user.getUserId();
|
||||
RedisUtil.set(newDistanceKey, distanceMap);
|
||||
// RedisUtil.del(locationKey);
|
||||
// RedisUtil.del(distanceKey);
|
||||
log.info("用户登录成功,缓存数据从sessionId[{}]迁移到userId[{}]",
|
||||
user.getSessionId(), user.getUserId());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("迁移位置缓存数据失败", e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
UserInfoInTokenBO userInfoInToken = new UserInfoInTokenBO();
|
||||
userInfoInToken.setUserId(user.getUserId());
|
||||
userInfoInToken.setSysType(SysTypeEnum.ORDINARY.value());
|
||||
userInfoInToken.setEnabled(user.getStatus() == 1);
|
||||
userInfoInToken.setOpenid(wxInfo.getOpenid());
|
||||
// 存储token返回vo
|
||||
tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken);
|
||||
} catch (Exception e) {
|
||||
result = "";
|
||||
e.printStackTrace();
|
||||
}
|
||||
return ServerResponseEntity.success(tokenInfoVO);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解密数据
|
||||
*
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
|
||||
/**
|
||||
* 解密数据
|
||||
*
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@PostMapping("/wxMerchantLogin")
|
||||
@Transactional
|
||||
public ServerResponseEntity<TokenInfoVO> wxMerchantLogin(@RequestBody Phone phone) {
|
||||
String result = "";
|
||||
Environment environment = applicationContext.getEnvironment();
|
||||
String appId = environment.getProperty("weixin.merchantappid");
|
||||
String secret = environment.getProperty("weixin.merchantsecret");
|
||||
TokenInfoVO tokenInfoVO = new TokenInfoVO();
|
||||
|
||||
//User user = userService.selectUserByOpenId(phone.getOpenid());
|
||||
String accessToken = weiXinUtil.postToken(appId, secret);
|
||||
WxInfo wxInfo = getWeChatMerchantOpenId(phone.getOpenIdCode());
|
||||
String tel = weiXinService.getUserPhoneNumber(accessToken, phone.getPhoneCode());
|
||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(User::getUserMobile, tel);
|
||||
queryWrapper.eq(User::getUserType, 2);
|
||||
User mUser = userService.getOne(queryWrapper);
|
||||
if (mUser == null) {
|
||||
return ServerResponseEntity.showFailMsg("您未注册商户");
|
||||
|
||||
/* User user = new User();
|
||||
String userId = IdUtil.simpleUUID();
|
||||
Date now = new Date();
|
||||
user.setModifyTime(now);
|
||||
user.setUserRegtime(now);
|
||||
user.setUserMobile(tel);
|
||||
user.setUserType(2);
|
||||
user.setOpenid(wxInfo.getOpenid());
|
||||
user.setUnionid(wxInfo.getUnionid());
|
||||
user.setUserId(userId);
|
||||
user.setStatus(1);
|
||||
userService.save(user);*/
|
||||
}else {
|
||||
mUser.setOpenid(wxInfo.getOpenid());
|
||||
mUser.setUnionid(wxInfo.getUnionid());
|
||||
userService.updateById(mUser);
|
||||
}
|
||||
try {
|
||||
UserInfoInTokenBO userInfoInToken = new UserInfoInTokenBO();
|
||||
userInfoInToken.setUserId(mUser.getUserId());
|
||||
userInfoInToken.setSysType(SysTypeEnum.ORDINARY.value());
|
||||
userInfoInToken.setEnabled(mUser.getStatus() == 1);
|
||||
userInfoInToken.setOpenid(wxInfo.getOpenid());
|
||||
// 存储token返回vo
|
||||
tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken);
|
||||
} catch (Exception e) {
|
||||
result = "";
|
||||
e.printStackTrace();
|
||||
}
|
||||
return ServerResponseEntity.success(tokenInfoVO);
|
||||
}
|
||||
|
||||
/* @PostMapping("/wxMerchantLogin")
|
||||
@Transactional
|
||||
public ServerResponseEntity<TokenInfoVO> wxMerchantLogin(@RequestBody Phone phone) {
|
||||
String result = "";
|
||||
Environment environment = applicationContext.getEnvironment();
|
||||
String appId = environment.getProperty("weixin.merchantappid");
|
||||
String secret = environment.getProperty("weixin.merchantsecret");
|
||||
TokenInfoVO tokenInfoVO = new TokenInfoVO();
|
||||
//User user = userService.selectUserByOpenId(phone.getOpenid());
|
||||
String accessToken = weiXinUtil.postToken(appId, secret);
|
||||
WxInfo wxInfo = getWeChatOpenId(phone.getOpenIdCode());
|
||||
String tel = weiXinService.getUserPhoneNumber(accessToken, phone.getPhoneCode());
|
||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(User::getUserMobile, tel);
|
||||
queryWrapper.eq(User::getUserType, 2);
|
||||
User mUser = userService.getOne(queryWrapper);
|
||||
if (mUser == null) {
|
||||
return ServerResponseEntity.showFailMsg("您未注册商户");
|
||||
}
|
||||
mUser.setOpenid(wxInfo.getOpenid());
|
||||
mUser.setUnionid(wxInfo.getUnionid());
|
||||
userService.updateById(mUser);
|
||||
try {
|
||||
UserInfoInTokenBO userInfoInToken = new UserInfoInTokenBO();
|
||||
userInfoInToken.setUserId(mUser.getUserId());
|
||||
userInfoInToken.setSysType(SysTypeEnum.ORDINARY.value());
|
||||
userInfoInToken.setEnabled(mUser.getStatus() == 1);
|
||||
userInfoInToken.setOpenid(wxInfo.getOpenid());
|
||||
// 存储token返回vo
|
||||
tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken);
|
||||
} catch (Exception e) {
|
||||
result = "";
|
||||
e.printStackTrace();
|
||||
}
|
||||
return ServerResponseEntity.success(tokenInfoVO);
|
||||
}*/
|
||||
|
||||
|
||||
public static WxInfo getWeChatOpenId(String code) {
|
||||
Environment environment = applicationContext.getEnvironment();
|
||||
// String url = "https://api.weixin.qq.com/sns/jscode2session?appid=wx2021d20e26e151ad&secret=131ba5acfd02fbd4cf93779a11b5579e&js_code=" + code + "&grant_type=authorization_code";
|
||||
String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + environment.getProperty("weixin.appid") + "&secret=" + environment.getProperty("weixin.secret") + "&js_code=" + code + "&grant_type=authorization_code";
|
||||
try {
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
Response response = client.newCall(request).execute();
|
||||
String responseData = response.body().string();
|
||||
System.out.println("responseData+" + responseData);
|
||||
/*if (responseData.contains("openid")) {
|
||||
String openid = responseData.substring(responseData.indexOf("openid") + 7, responseData.length() - 1);
|
||||
return openid;
|
||||
} else {
|
||||
return "遇见某种问题或空值";
|
||||
}*/
|
||||
WxInfo wxInfo = JSON.parseObject(responseData, WxInfo.class);
|
||||
return wxInfo;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static WxInfo getWeChatMerchantOpenId(String code) {
|
||||
Environment environment = applicationContext.getEnvironment();
|
||||
// String url = "https://api.weixin.qq.com/sns/jscode2session?appid=wx2021d20e26e151ad&secret=131ba5acfd02fbd4cf93779a11b5579e&js_code=" + code + "&grant_type=authorization_code";
|
||||
String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + environment.getProperty("weixin.merchantappid") + "&secret=" + environment.getProperty("weixin.merchantsecret") + "&js_code=" + code + "&grant_type=authorization_code";
|
||||
try {
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
Response response = client.newCall(request).execute();
|
||||
String responseData = response.body().string();
|
||||
System.out.println("responseData+" + responseData);
|
||||
/*if (responseData.contains("openid")) {
|
||||
String openid = responseData.substring(responseData.indexOf("openid") + 7, responseData.length() - 1);
|
||||
return openid;
|
||||
} else {
|
||||
return "遇见某种问题或空值";
|
||||
}*/
|
||||
WxInfo wxInfo = JSON.parseObject(responseData, WxInfo.class);
|
||||
return wxInfo;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String getStringRandom(int length) {
|
||||
String val = "";
|
||||
Random random = new Random();
|
||||
//参数length,表明生成几位随机数
|
||||
for (int i = 0; i < length; i++) {
|
||||
String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";
|
||||
//输出字母仍是数字
|
||||
if ("char".equalsIgnoreCase(charOrNum)) {
|
||||
//输出是大写字母仍是小写字母
|
||||
int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;
|
||||
val += (char) (random.nextInt(26) + temp);
|
||||
} else if ("num".equalsIgnoreCase(charOrNum)) {
|
||||
val += String.valueOf(random.nextInt(10));
|
||||
}
|
||||
}
|
||||
return "wx" + val;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.pxdj.security.api.wx;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
|
||||
/**
|
||||
* @author wut
|
||||
* @PackageName:com.szbt.framework.security.wx
|
||||
* @ClassName:WxCodeAuthenticationProvider
|
||||
* @Description: wx登陆鉴权 Provider,要求实现 AuthenticationProvider 接口
|
||||
* @date 2023/6/12 10:47
|
||||
*/
|
||||
public class WxCodeAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
WxCodeAuthenticationToken authenticationToken = (WxCodeAuthenticationToken) authentication;
|
||||
|
||||
String openId = (String) authenticationToken.getPrincipal();
|
||||
|
||||
UserDetails userDetails = userDetailsService.loadUserByUsername(openId);
|
||||
|
||||
// 此时鉴权成功后,应当重新 new 一个拥有鉴权的 authenticationResult 返回
|
||||
WxCodeAuthenticationToken authenticationResult = new WxCodeAuthenticationToken(userDetails, userDetails.getAuthorities());
|
||||
|
||||
authenticationResult.setDetails(authenticationToken.getDetails());
|
||||
|
||||
return authenticationResult;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
// 判断 authentication 是不是 WxCodeAuthenticationToken 的子类或子接口
|
||||
return WxCodeAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
|
||||
public UserDetailsService getUserDetailsService() {
|
||||
return userDetailsService;
|
||||
}
|
||||
|
||||
public void setUserDetailsService(UserDetailsService userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.pxdj.security.api.wx;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author wut
|
||||
* @PackageName:com.szbt.framework.security.authentication
|
||||
* @ClassName:WxCodeAuthenticationToken
|
||||
* @Description: 自定义微信登录token验证
|
||||
* @date 2023/6/12 10:40
|
||||
*/
|
||||
public class WxCodeAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||
|
||||
/**
|
||||
* 在 UsernamePasswordAuthenticationToken 中该字段代表登录的用户名,
|
||||
* 在这里就代表登录的openId
|
||||
*/ private final Object principal;
|
||||
|
||||
/**
|
||||
* 构建一个没有鉴权的 WxCodeAuthenticationToken
|
||||
*/ public WxCodeAuthenticationToken(Object principal) {
|
||||
super(null);
|
||||
this.principal = principal;
|
||||
setAuthenticated(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建拥有鉴权的 WxCodeAuthenticationToken
|
||||
*/
|
||||
public WxCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
|
||||
super(authorities);
|
||||
this.principal = principal;
|
||||
// must use super, as we override
|
||||
super.setAuthenticated(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
|
||||
if (isAuthenticated) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
|
||||
}
|
||||
|
||||
super.setAuthenticated(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eraseCredentials() {
|
||||
super.eraseCredentials();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.pxdj.security.api.wx;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/foods/food")
|
||||
public class WxIdController {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.pxdj.security.api.wx;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
/**
|
||||
* 微信小程序加解密
|
||||
* @author liuyazhuang
|
||||
*
|
||||
*/
|
||||
public class WxPKCS7Encoder {
|
||||
private static final Charset CHARSET = Charset.forName("utf-8");
|
||||
private static final int BLOCK_SIZE = 32;
|
||||
|
||||
/**
|
||||
* 获得对明文进行补位填充的字节.
|
||||
*
|
||||
* @param count
|
||||
* 需要进行填充补位操作的明文字节个数
|
||||
* @return 补齐用的字节数组
|
||||
*/
|
||||
public static byte[] encode(int count) {
|
||||
// 计算需要填充的位数
|
||||
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
|
||||
if (amountToPad == 0) {
|
||||
amountToPad = BLOCK_SIZE;
|
||||
}
|
||||
// 获得补位所用的字符
|
||||
char padChr = chr(amountToPad);
|
||||
String tmp = new String();
|
||||
for (int index = 0; index < amountToPad; index++) {
|
||||
tmp += padChr;
|
||||
}
|
||||
return tmp.getBytes(CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除解密后明文的补位字符
|
||||
*
|
||||
* @param decrypted
|
||||
* 解密后的明文
|
||||
* @return 删除补位字符后的明文
|
||||
*/
|
||||
public static byte[] decode(byte[] decrypted) {
|
||||
int pad = decrypted[decrypted.length - 1];
|
||||
if (pad < 1 || pad > 32) {
|
||||
pad = 0;
|
||||
}
|
||||
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数字转化成ASCII码对应的字符,用于对明文进行补码
|
||||
*
|
||||
* @param a
|
||||
* 需要转化的数字
|
||||
* @return 转化得到的字符
|
||||
*/
|
||||
public static char chr(int a) {
|
||||
byte target = (byte) (a & 0xFF);
|
||||
return (char) target;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.pxdj.security.api.wx;
|
||||
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.pxdj.api.domain.User;
|
||||
import com.pxdj.security.api.model.WxInfo;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static com.pxdj.common.utils.SpringContextUtils.applicationContext;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/wx")
|
||||
public class wx {
|
||||
@PostMapping("/getWeChatOpenId")
|
||||
public static String getWeChatOpenId( @RequestBody String code) {
|
||||
String[] split = code.split(":");
|
||||
String s = split[1].toString();
|
||||
String substring = s.substring(1, s.length() - 2);
|
||||
code=substring;
|
||||
Environment environment = applicationContext.getEnvironment();
|
||||
// String url = "https://api.weixin.qq.com/sns/jscode2session?appid=wx2021d20e26e151ad&secret=131ba5acfd02fbd4cf93779a11b5579e&js_code=" + code + "&grant_type=authorization_code";
|
||||
String url = "https://api.weixin.qq.com/sns/jscode2session?appid="+environment.getProperty("weixin.appid")+"&secret="+environment.getProperty("weixin.secret")+"&js_code=" + code + "&grant_type=authorization_code";
|
||||
try {
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
Response response = client.newCall(request).execute();
|
||||
String responseData = response.body().string();
|
||||
System.out.println("responseData+"+responseData);
|
||||
/*if (responseData.contains("openid")) {
|
||||
String openid = responseData.substring(responseData.indexOf("openid") + 7, responseData.length() - 1);
|
||||
return openid;
|
||||
} else {
|
||||
return "遇见某种问题或空值";
|
||||
}*/
|
||||
WxInfo wxInfo = JSON.parseObject(responseData, WxInfo.class);
|
||||
return JSON.toJSONString(wxInfo);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@PostMapping("/getWeChatMerchantOpenId")
|
||||
public static String getWeChatMerchantOpenId( @RequestBody String code) {
|
||||
String[] split = code.split(":");
|
||||
String s = split[1].toString();
|
||||
String substring = s.substring(1, s.length() - 2);
|
||||
code=substring;
|
||||
Environment environment = applicationContext.getEnvironment();
|
||||
// String url = "https://api.weixin.qq.com/sns/jscode2session?appid=wx2021d20e26e151ad&secret=131ba5acfd02fbd4cf93779a11b5579e&js_code=" + code + "&grant_type=authorization_code";
|
||||
String url = "https://api.weixin.qq.com/sns/jscode2session?appid="+environment.getProperty("weixin.merchantappid")+"&secret="+environment.getProperty("weixin.merchantsecret")+"&js_code=" + code + "&grant_type=authorization_code";
|
||||
//wxMaService.getUserService().getSessionInfo(loginParam.getCode());
|
||||
try {
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
Response response = client.newCall(request).execute();
|
||||
String responseData = response.body().string();
|
||||
System.out.println("responseData+"+responseData);
|
||||
/*if (responseData.contains("openid")) {
|
||||
String openid = responseData.substring(responseData.indexOf("openid") + 7, responseData.length() - 1);
|
||||
return openid;
|
||||
} else {
|
||||
return "遇见某种问题或空值";
|
||||
}*/
|
||||
WxInfo wxInfo = JSON.parseObject(responseData, WxInfo.class);
|
||||
return JSON.toJSONString(wxInfo);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
pxdj-java/pxdj-security/pxdj-security-common/pom.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?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">
|
||||
<parent>
|
||||
<groupId>com.pxdj</groupId>
|
||||
<artifactId>pxdj-security</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>pxdj-security-common</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.pxdj</groupId>
|
||||
<artifactId>pxdj-service</artifactId>
|
||||
<version>${pxdj.version}</version>
|
||||
</dependency>
|
||||
<!--security-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.anji-plus</groupId>
|
||||
<artifactId>captcha</artifactId>
|
||||
<version>1.3.0</version>
|
||||
</dependency>
|
||||
<!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.34.0</version>
|
||||
</dependency>
|
||||
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-redis-jackson</artifactId>
|
||||
<version>${satoken.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>${fastjson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- <dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>-->
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.pxdj.common.adapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 实现该接口之后,修改需要授权登陆的路径,不需要授权登陆的路径
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/25 17:31
|
||||
*/
|
||||
public interface AuthConfigAdapter {
|
||||
/**
|
||||
* 也许需要登录才可用的url
|
||||
*/
|
||||
String MAYBE_AUTH_URI = "/**/ma/**";
|
||||
|
||||
/**
|
||||
* 需要授权登陆的路径
|
||||
* @return 需要授权登陆的路径列表
|
||||
*/
|
||||
List<String> pathPatterns();
|
||||
|
||||
/**
|
||||
* 不需要授权登陆的路径
|
||||
* @return 不需要授权登陆的路径列表
|
||||
*/
|
||||
List<String> excludePathPatterns();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.pxdj.common.adapter;
|
||||
|
||||
import com.anji.captcha.service.CaptchaCacheService;
|
||||
import com.pxdj.common.utils.RedisUtil;
|
||||
|
||||
/**
|
||||
* 适配验证码在redis的存储
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/25 17:33
|
||||
*/
|
||||
public class CaptchaCacheServiceRedisImpl implements CaptchaCacheService {
|
||||
@Override
|
||||
public void set(String key, String value, long expiresInSeconds) {
|
||||
RedisUtil.set(key, value, expiresInSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String key) {
|
||||
return RedisUtil.hasKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String key) {
|
||||
RedisUtil.del(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String key) {
|
||||
return RedisUtil.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "redis";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.pxdj.common.adapter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/25 17:33
|
||||
*/
|
||||
public class DefaultAuthConfigAdapter implements AuthConfigAdapter {
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultAuthConfigAdapter.class);
|
||||
|
||||
|
||||
public static final List<String> EXCLUDE_PATH = Arrays.asList(
|
||||
"/webjars/**",
|
||||
"/swagger/**",
|
||||
"/v3/api-docs/**",
|
||||
"/doc.html",
|
||||
"/swagger-ui.html",
|
||||
"/swagger-ui/**",
|
||||
"/swagger-resources/**",
|
||||
"/captcha/**",
|
||||
"/p/pay/**",
|
||||
"/wx/getWeChatOpenId",
|
||||
"/p/order/test",
|
||||
"/p/merchant/occupancy",
|
||||
"/p/area/listByPid",
|
||||
|
||||
//"/p/user/activation",
|
||||
/* "/merchantNotLogin/getMerchantListTest",*/
|
||||
"/mp/serverCheck",
|
||||
"/wxcore/wxLogin",
|
||||
"/adminLogin",
|
||||
"/mall4j/img/**");
|
||||
|
||||
public DefaultAuthConfigAdapter() {
|
||||
logger.info("not implement other AuthConfigAdapter, use DefaultAuthConfigAdapter... all url need auth...");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> pathPatterns() {
|
||||
return Collections.singletonList("/*");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> excludePathPatterns() {
|
||||
return EXCLUDE_PATH;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.pxdj.common.adapter;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.cors.CorsUtils;
|
||||
|
||||
/**
|
||||
* 使用security的防火墙功能,但不使用security的认证授权登录
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/25 17:33
|
||||
*/
|
||||
@Component
|
||||
@EnableWebSecurity
|
||||
public class MallWebSecurityConfigurerAdapter {
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
return http
|
||||
.csrf().disable() // 禁用 CSRF
|
||||
.cors() // 启用 CORS
|
||||
.and()
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态会话
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll() // 允许预检请求
|
||||
.antMatchers("/**").permitAll() // 允许所有请求
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
/*public class MallWebSecurityConfigurerAdapter {
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
|
||||
// We don't need CSRF for token based authentication
|
||||
return http.csrf().disable().cors()
|
||||
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and().authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
|
||||
.and()
|
||||
.authorizeRequests().requestMatchers("/**").permitAll().and().build();
|
||||
}
|
||||
|
||||
}*/
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.bo;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* token信息,该信息存在redis中
|
||||
*
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/25 17:33
|
||||
*/
|
||||
@Data
|
||||
public class TokenInfoBO {
|
||||
|
||||
/**
|
||||
* 保存在token信息里面的用户信息
|
||||
*/
|
||||
private UserInfoInTokenBO userInfoInToken;
|
||||
|
||||
private String accessToken;
|
||||
|
||||
private String refreshToken;
|
||||
|
||||
/**
|
||||
* 在多少秒后过期
|
||||
*/
|
||||
private Integer expiresIn;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.bo;
|
||||
|
||||
import com.pxdj.common.enums.SysTypeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 保存在token信息里面的用户信息
|
||||
*
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/25 17:33
|
||||
*/
|
||||
@Data
|
||||
public class UserInfoInTokenBO {
|
||||
|
||||
/**
|
||||
* 用户在自己系统的用户id
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 租户id (商家id)
|
||||
*/
|
||||
private Long shopId;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
private String nickName;
|
||||
|
||||
/**
|
||||
* 系统类型
|
||||
* @see SysTypeEnum
|
||||
*/
|
||||
private Integer sysType;
|
||||
|
||||
/**
|
||||
* 是否是管理员
|
||||
*/
|
||||
private Integer isAdmin;
|
||||
|
||||
private String bizUserId;
|
||||
/**
|
||||
* 微信opendid
|
||||
*/
|
||||
private String openid;
|
||||
/**
|
||||
* 权限列表
|
||||
*/
|
||||
private Set<String> perms;
|
||||
|
||||
/**
|
||||
* 状态 1 正常 0 无效
|
||||
*/
|
||||
private Boolean enabled;
|
||||
|
||||
/**
|
||||
* 其他Id
|
||||
*/
|
||||
private Long otherId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
|
||||
package com.pxdj.common.config;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.pxdj.common.adapter.AuthConfigAdapter;
|
||||
import com.pxdj.common.adapter.DefaultAuthConfigAdapter;
|
||||
import com.pxdj.common.filter.AuthFilter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
|
||||
/**
|
||||
* 授权配置
|
||||
*
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/25 17:33
|
||||
*/
|
||||
@Configuration
|
||||
public class AuthConfig {
|
||||
|
||||
@Autowired
|
||||
private AuthFilter authFilter;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public AuthConfigAdapter authConfigAdapter() {
|
||||
return new DefaultAuthConfigAdapter();
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
public FilterRegistrationBean<AuthFilter> filterRegistration(AuthConfigAdapter authConfigAdapter) {
|
||||
FilterRegistrationBean<AuthFilter> registration = new FilterRegistrationBean<>();
|
||||
// 添加过滤器
|
||||
registration.setFilter(authFilter);
|
||||
// 设置过滤路径,/*所有路径
|
||||
registration.addUrlPatterns(ArrayUtil.toArray(authConfigAdapter.pathPatterns(), String.class));
|
||||
registration.setName("authFilter");
|
||||
// 设置优先级
|
||||
registration.setOrder(0);
|
||||
registration.setDispatcherTypes(DispatcherType.REQUEST);
|
||||
return registration;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.config;
|
||||
|
||||
import com.anji.captcha.model.common.CaptchaTypeEnum;
|
||||
import com.anji.captcha.model.common.Const;
|
||||
import com.anji.captcha.service.CaptchaService;
|
||||
import com.anji.captcha.service.impl.CaptchaServiceFactory;
|
||||
import com.anji.captcha.util.ImageUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.util.Base64Utils;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* 这里把验证码的底图存入redis中,如果报获取验证码失败找管理员什么的可以看下redis的情况
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/25 17:33
|
||||
*/
|
||||
@Configuration
|
||||
public class CaptchaConfig {
|
||||
|
||||
@Bean
|
||||
public CaptchaService captchaService() {
|
||||
Properties config = new Properties();
|
||||
config.put(Const.CAPTCHA_CACHETYPE, "redis");
|
||||
config.put(Const.CAPTCHA_WATER_MARK, "");
|
||||
// 滑动验证
|
||||
config.put(Const.CAPTCHA_TYPE, CaptchaTypeEnum.BLOCKPUZZLE.getCodeValue());
|
||||
config.put(Const.CAPTCHA_INIT_ORIGINAL, "true");
|
||||
initializeBaseMap();
|
||||
return CaptchaServiceFactory.getInstance(config);
|
||||
}
|
||||
|
||||
private static void initializeBaseMap() {
|
||||
ImageUtils.cacheBootImage(getResourcesImagesFile("classpath:captcha" + "/original/*.png"), getResourcesImagesFile("classpath:captcha" + "/slidingBlock/*.png"), Collections.emptyMap());
|
||||
}
|
||||
|
||||
public static Map<String, String> getResourcesImagesFile(String path) {
|
||||
Map<String, String> imgMap = new HashMap<>(16);
|
||||
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||
|
||||
try {
|
||||
Resource[] resources = resolver.getResources(path);
|
||||
Resource[] var4 = resources;
|
||||
int var5 = resources.length;
|
||||
|
||||
for(int var6 = 0; var6 < var5; ++var6) {
|
||||
Resource resource = var4[var6];
|
||||
byte[] bytes = FileCopyUtils.copyToByteArray(resource.getInputStream());
|
||||
String string = Base64Utils.encodeToString(bytes);
|
||||
String filename = resource.getFilename();
|
||||
imgMap.put(filename, string);
|
||||
}
|
||||
} catch (Exception var11) {
|
||||
var11.printStackTrace();
|
||||
}
|
||||
|
||||
return imgMap;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.pxdj.common.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
/**
|
||||
* @author yami
|
||||
*/
|
||||
@Configuration
|
||||
public class CorsConfig {
|
||||
|
||||
/**
|
||||
* 修改为添加而不是设置,* 最好生产环境改为实际的需要, 这里可以用多个add配置多个域名
|
||||
* configuration.addAllowedOrigin("http://localhost:8080");
|
||||
* configuration.addAllowedOrigin("http://192.168.1.6:8080");
|
||||
* @return CorsConfigurationSource
|
||||
*/
|
||||
@Bean
|
||||
public CorsConfigurationSource corsConfigurationSource() {
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
configuration.addAllowedOriginPattern("*");
|
||||
//修改为添加而不是设置
|
||||
configuration.addAllowedMethod("*");
|
||||
//这里很重要,起码需要允许 Access-Control-Allow-Origin
|
||||
configuration.addAllowedHeader("*");
|
||||
configuration.setAllowCredentials(true);
|
||||
configuration.setMaxAge(3600 * 24L);
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", configuration);
|
||||
return source;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
/**
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/25 17:33
|
||||
*/
|
||||
@Configuration
|
||||
public class PasswordConfig {
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder(){
|
||||
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.controller;
|
||||
|
||||
import com.anji.captcha.model.common.RepCodeEnum;
|
||||
import com.anji.captcha.model.common.ResponseModel;
|
||||
import com.anji.captcha.model.vo.CaptchaVO;
|
||||
import com.anji.captcha.service.CaptchaService;
|
||||
import com.pxdj.common.response.ServerResponseEntity;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/25 17:33
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/captcha")
|
||||
@Tag(name = "验证码")
|
||||
public class CaptchaController {
|
||||
|
||||
private final CaptchaService captchaService;
|
||||
|
||||
public CaptchaController(CaptchaService captchaService) {
|
||||
this.captchaService = captchaService;
|
||||
}
|
||||
|
||||
@PostMapping({ "/get" })
|
||||
public ServerResponseEntity<ResponseModel> get(@RequestBody CaptchaVO captchaVO) {
|
||||
return ServerResponseEntity.success(captchaService.get(captchaVO));
|
||||
}
|
||||
|
||||
@PostMapping({ "/check" })
|
||||
public ServerResponseEntity<ResponseModel> check(@RequestBody CaptchaVO captchaVO) {
|
||||
ResponseModel responseModel;
|
||||
try {
|
||||
responseModel = captchaService.check(captchaVO);
|
||||
}catch (Exception e) {
|
||||
return ServerResponseEntity.success(ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_COORDINATE_ERROR));
|
||||
}
|
||||
return ServerResponseEntity.success(responseModel);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.controller;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pxdj.common.response.ServerResponseEntity;
|
||||
import com.pxdj.common.manager.TokenStore;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
|
||||
/**
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/25 17:33
|
||||
*/
|
||||
@RestController
|
||||
@Tag(name = "注销")
|
||||
public class LogoutController {
|
||||
|
||||
@Autowired
|
||||
private TokenStore tokenStore;
|
||||
|
||||
@PostMapping("/logOut")
|
||||
@Operation(summary = "退出登陆" , description = "点击退出登陆,清除token,清除菜单缓存")
|
||||
public ServerResponseEntity<Void> logOut(HttpServletRequest request) {
|
||||
String accessToken = request.getHeader("Authorization");
|
||||
if (StrUtil.isBlank(accessToken)) {
|
||||
return ServerResponseEntity.success();
|
||||
}
|
||||
// 删除该用户在该系统当前的token
|
||||
tokenStore.deleteCurrentToken(accessToken);
|
||||
return ServerResponseEntity.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.controller;
|
||||
|
||||
import com.pxdj.common.bo.TokenInfoBO;
|
||||
import com.pxdj.common.dto.RefreshTokenDTO;
|
||||
import com.pxdj.common.manager.TokenStore;
|
||||
import com.pxdj.common.response.ServerResponseEntity;
|
||||
import com.pxdj.common.vo.TokenInfoVO;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
|
||||
/**
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/25 17:33
|
||||
*/
|
||||
@RestController
|
||||
@Tag(name = "token")
|
||||
public class TokenController {
|
||||
|
||||
@Autowired
|
||||
private TokenStore tokenStore;
|
||||
|
||||
@PostMapping("/token/refresh")
|
||||
public ServerResponseEntity<TokenInfoVO> refreshToken(@RequestBody RefreshTokenDTO refreshTokenDTO) {
|
||||
TokenInfoBO tokenInfoServerResponseEntity = tokenStore
|
||||
.refreshToken(refreshTokenDTO.getRefreshToken());
|
||||
return ServerResponseEntity
|
||||
.success(BeanUtil.copyProperties(tokenInfoServerResponseEntity, TokenInfoVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
/**
|
||||
* 用于登陆传递账号密码
|
||||
*
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/25 17:33
|
||||
*/
|
||||
@Data
|
||||
public class AuthenticationDTO {
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@Schema(description = "用户名/邮箱/手机号" , required = true)
|
||||
protected String userName;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@Schema(description = "一般用作密码" , required = true)
|
||||
protected String passWord;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
|
||||
/**
|
||||
* 刷新token
|
||||
*
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/25 17:33
|
||||
*/
|
||||
public class RefreshTokenDTO {
|
||||
|
||||
/**
|
||||
* refreshToken
|
||||
*/
|
||||
@Schema(description = "refreshToken" , required = true)
|
||||
private String refreshToken;
|
||||
|
||||
public String getRefreshToken() {
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
public void setRefreshToken(String refreshToken) {
|
||||
this.refreshToken = refreshToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RefreshTokenDTO{" + "refreshToken='" + refreshToken + '\'' + '}';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.enums;
|
||||
|
||||
/**
|
||||
* 系统类型
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/25 17:33
|
||||
*/
|
||||
public enum SysTypeEnum {
|
||||
|
||||
/**
|
||||
* 普通用户系统
|
||||
*/
|
||||
ORDINARY(0),
|
||||
|
||||
/**
|
||||
* 后台
|
||||
*/
|
||||
ADMIN(1),
|
||||
;
|
||||
|
||||
private final Integer value;
|
||||
|
||||
public Integer value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
SysTypeEnum(Integer value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.filter;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pxdj.common.exception.YamiShopBindException;
|
||||
import com.pxdj.common.handler.HttpHandler;
|
||||
import com.pxdj.common.response.*;
|
||||
import com.pxdj.common.adapter.AuthConfigAdapter;
|
||||
import com.pxdj.common.bo.UserInfoInTokenBO;
|
||||
import com.pxdj.common.manager.TokenStore;
|
||||
import com.pxdj.common.util.AuthUserContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 授权过滤,只要实现AuthConfigAdapter接口,添加对应路径即可:
|
||||
*
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/3/25 17:33
|
||||
*/
|
||||
@Component
|
||||
public class AuthFilter implements Filter {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthFilter.class);
|
||||
|
||||
@Autowired
|
||||
private AuthConfigAdapter authConfigAdapter;
|
||||
|
||||
@Autowired
|
||||
private HttpHandler httpHandler;
|
||||
|
||||
@Autowired
|
||||
private TokenStore tokenStore;
|
||||
|
||||
@Value("${sa-token.token-name}")
|
||||
private String tokenName;
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
HttpServletResponse resp = (HttpServletResponse) response;
|
||||
|
||||
String requestUri = req.getRequestURI();
|
||||
|
||||
List<String> excludePathPatterns = authConfigAdapter.excludePathPatterns();
|
||||
/* if(excludePathPatterns.size()==0){
|
||||
|
||||
}
|
||||
excludePathPatterns.add("/p/user/getVipPackageList");*/
|
||||
AntPathMatcher pathMatcher = new AntPathMatcher();
|
||||
// 如果匹配不需要授权的路径,就不需要校验是否需要授权
|
||||
if (CollectionUtil.isNotEmpty(excludePathPatterns)) {
|
||||
for (String excludePathPattern : excludePathPatterns) {
|
||||
if (pathMatcher.match(excludePathPattern, requestUri)) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String accessToken = req.getHeader(tokenName);
|
||||
// 也许需要登录,不登陆也能用的uri
|
||||
boolean mayAuth = pathMatcher.match(AuthConfigAdapter.MAYBE_AUTH_URI, requestUri);
|
||||
UserInfoInTokenBO userInfoInToken = null;
|
||||
try {
|
||||
// 如果有token,就要获取token
|
||||
if (StrUtil.isNotBlank(accessToken)) {
|
||||
// 校验登录,并从缓存中取出用户信息
|
||||
try {
|
||||
StpUtil.checkLogin();
|
||||
} catch (Exception e) {
|
||||
httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED));
|
||||
return;
|
||||
}
|
||||
userInfoInToken = tokenStore.getUserInfoByAccessToken(accessToken, true);
|
||||
}
|
||||
else if (!mayAuth) {
|
||||
// 返回前端401
|
||||
httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED));
|
||||
return;
|
||||
}
|
||||
// 保存上下文
|
||||
AuthUserContext.set(userInfoInToken);
|
||||
chain.doFilter(req, resp);
|
||||
}catch (Exception e) {
|
||||
// 手动捕获下非controller异常
|
||||
if (e instanceof YamiShopBindException) {
|
||||
httpHandler.printServerResponseToWeb((YamiShopBindException) e);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
AuthUserContext.clean();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.manager;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pxdj.common.exception.YamiShopBindException;
|
||||
import com.pxdj.common.utils.IpHelper;
|
||||
import com.pxdj.common.utils.RedisUtil;
|
||||
|
||||
import com.pxdj.common.enums.SysTypeEnum;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @date 2022/3/25 17:33
|
||||
* @author lh
|
||||
*/
|
||||
@Component
|
||||
public class PasswordCheckManager {
|
||||
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
/**
|
||||
* 半小时内最多错误10次
|
||||
*/
|
||||
private static final int TIMES_CHECK_INPUT_PASSWORD_NUM = 10;
|
||||
|
||||
/**
|
||||
* 检查用户输入错误的验证码次数
|
||||
*/
|
||||
private static final String CHECK_VALID_CODE_NUM_PREFIX = "checkUserInputErrorPassword_";
|
||||
public void checkPassword(SysTypeEnum sysTypeEnum,String userNameOrMobile, String rawPassword, String encodedPassword) {
|
||||
|
||||
String checkPrefix = sysTypeEnum.value() + CHECK_VALID_CODE_NUM_PREFIX + IpHelper.getIpAddr();
|
||||
|
||||
int count = 0;
|
||||
if(RedisUtil.hasKey(checkPrefix + userNameOrMobile)){
|
||||
count = RedisUtil.get(checkPrefix + userNameOrMobile);
|
||||
}
|
||||
if(count > TIMES_CHECK_INPUT_PASSWORD_NUM){
|
||||
throw new YamiShopBindException("密码输入错误十次,已限制登录30分钟");
|
||||
}
|
||||
// 半小时后失效
|
||||
RedisUtil.set(checkPrefix + userNameOrMobile,count,1800);
|
||||
// 密码不正确
|
||||
if (StrUtil.isBlank(encodedPassword) || !passwordEncoder.matches(rawPassword,encodedPassword)){
|
||||
count++;
|
||||
// 半小时后失效
|
||||
RedisUtil.set(checkPrefix + userNameOrMobile,count,1800);
|
||||
throw new YamiShopBindException("账号或密码不正确");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.manager;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.symmetric.AES;
|
||||
import com.pxdj.common.exception.YamiShopBindException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author 菠萝凤梨
|
||||
* @date 2022/1/19 16:02
|
||||
*/
|
||||
@Component
|
||||
public class PasswordManager {
|
||||
private static final Logger logger = LoggerFactory.getLogger(PasswordManager.class);
|
||||
|
||||
/**
|
||||
* 用于aes签名的key,16位
|
||||
*/
|
||||
@Value("${auth.password.signKey:-mall4j-password}")
|
||||
public String passwordSignKey;
|
||||
|
||||
public String decryptPassword(String data) {
|
||||
// 在使用oracle的JDK时,JAR包必须签署特殊的证书才能使用。
|
||||
// 解决方案 1.使用openJDK或者非oracle的JDK(建议) 2.添加证书
|
||||
// hutool的aes报错可以打开下面那段代码
|
||||
SecureUtil.disableBouncyCastle();
|
||||
AES aes = new AES(passwordSignKey.getBytes(StandardCharsets.UTF_8));
|
||||
String decryptStr;
|
||||
String decryptPassword;
|
||||
try {
|
||||
decryptStr = aes.decryptStr(data);
|
||||
decryptPassword = decryptStr.substring(13);
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception:", e);
|
||||
throw new YamiShopBindException("AES解密错误", e);
|
||||
}
|
||||
return decryptPassword;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.manager;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.pxdj.common.constant.OauthCacheNames;
|
||||
import com.pxdj.common.exception.YamiShopBindException;
|
||||
import com.pxdj.common.response.ResponseEnum;
|
||||
import com.pxdj.common.bo.TokenInfoBO;
|
||||
import com.pxdj.common.bo.UserInfoInTokenBO;
|
||||
import com.pxdj.common.enums.SysTypeEnum;
|
||||
import com.pxdj.common.vo.TokenInfoVO;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* token管理 1. 登陆返回token 2. 刷新token 3. 清除用户过去token 4. 校验token
|
||||
*
|
||||
* @author FrozenWatermelon
|
||||
* @date 2020/7/2
|
||||
*/
|
||||
@Component
|
||||
public class TokenStore {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TokenStore.class);
|
||||
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
public TokenStore(RedisTemplate<String, Object> redisTemplate) {
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以Sa-Token技术生成token,并返回token信息
|
||||
* @param userInfoInToken
|
||||
* @return
|
||||
*/
|
||||
public TokenInfoBO storeAccessSaToken(UserInfoInTokenBO userInfoInToken) {
|
||||
// token生成
|
||||
int timeoutSecond = getExpiresIn(userInfoInToken.getSysType());
|
||||
String uid = this.getUid(userInfoInToken.getSysType().toString(), userInfoInToken.getUserId());
|
||||
StpUtil.login(uid, timeoutSecond);
|
||||
String token = StpUtil.getTokenValue();
|
||||
// 用户信息存入缓存
|
||||
String keyName = OauthCacheNames.USER_INFO + token;
|
||||
redisTemplate.delete(keyName);
|
||||
redisTemplate.opsForValue().set(
|
||||
keyName,
|
||||
JSON.toJSONString(userInfoInToken),
|
||||
timeoutSecond,
|
||||
TimeUnit.SECONDS
|
||||
);
|
||||
// 数据封装返回(token不用加密)
|
||||
TokenInfoBO tokenInfoBO = new TokenInfoBO();
|
||||
tokenInfoBO.setUserInfoInToken(userInfoInToken);
|
||||
tokenInfoBO.setExpiresIn(timeoutSecond);
|
||||
tokenInfoBO.setAccessToken(token);
|
||||
tokenInfoBO.setRefreshToken(token);
|
||||
return tokenInfoBO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算过期时间(单位:秒)
|
||||
* @param sysType
|
||||
* @return
|
||||
*/
|
||||
private int getExpiresIn(int sysType) {
|
||||
// 3600秒
|
||||
int expiresIn = 3600;
|
||||
// 普通用户token过期时间
|
||||
if (Objects.equals(sysType, SysTypeEnum.ORDINARY.value())) {
|
||||
expiresIn = expiresIn * 24 * 30;
|
||||
}
|
||||
// 系统管理员的token过期时间
|
||||
if (Objects.equals(sysType, SysTypeEnum.ADMIN.value())) {
|
||||
expiresIn = expiresIn * 24 * 30;
|
||||
}
|
||||
return expiresIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据accessToken 获取用户信息
|
||||
* @param accessToken accessToken
|
||||
* @param needDecrypt 是否需要解密
|
||||
* @return 用户信息
|
||||
*/
|
||||
public UserInfoInTokenBO getUserInfoByAccessToken(String accessToken, boolean needDecrypt) {
|
||||
if (StrUtil.isBlank(accessToken)) {
|
||||
throw new YamiShopBindException(ResponseEnum.UNAUTHORIZED,"accessToken is blank");
|
||||
}
|
||||
String keyName = OauthCacheNames.USER_INFO + accessToken;
|
||||
Object redisCache = redisTemplate.opsForValue().get(keyName);
|
||||
if (redisCache == null) {
|
||||
throw new YamiShopBindException(ResponseEnum.UNAUTHORIZED,"登录过期,请重新登录");
|
||||
}
|
||||
return JSON.parseObject(redisCache.toString(), UserInfoInTokenBO.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新token,并返回新的token
|
||||
* @param refreshToken
|
||||
* @return
|
||||
*/
|
||||
public TokenInfoBO refreshToken(String refreshToken) {
|
||||
if (StrUtil.isBlank(refreshToken)) {
|
||||
throw new YamiShopBindException(ResponseEnum.UNAUTHORIZED,"refreshToken is blank");
|
||||
}
|
||||
// 删除旧token
|
||||
UserInfoInTokenBO userInfoInTokenBO = getUserInfoByAccessToken(refreshToken, false);
|
||||
this.deleteCurrentToken(refreshToken);
|
||||
// 保存一份新的token
|
||||
return storeAccessSaToken(userInfoInTokenBO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定用户的全部的token
|
||||
*/
|
||||
public void deleteAllToken(String sysType, String userId) {
|
||||
// 删除用户缓存
|
||||
String uid = this.getUid(sysType, userId);
|
||||
List<String> tokens = StpUtil.getTokenValueListByLoginId(uid);
|
||||
if (!CollectionUtils.isEmpty(tokens)) {
|
||||
List<String> keyNames = new ArrayList<>();
|
||||
for (String token : tokens) {
|
||||
keyNames.add(OauthCacheNames.USER_INFO + token);
|
||||
}
|
||||
redisTemplate.delete(keyNames);
|
||||
}
|
||||
// 移除token
|
||||
StpUtil.logout(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成token,并返回token展示信息
|
||||
* @param userInfoInToken
|
||||
* @return
|
||||
*/
|
||||
public TokenInfoVO storeAndGetVo(UserInfoInTokenBO userInfoInToken) {
|
||||
if (!userInfoInToken.getEnabled()){
|
||||
// 用户已禁用,请联系客服
|
||||
throw new YamiShopBindException("用户已禁用,请联系客服");
|
||||
}
|
||||
TokenInfoBO tokenInfoBO = storeAccessSaToken(userInfoInToken);
|
||||
// 数据封装返回
|
||||
TokenInfoVO tokenInfoVO = new TokenInfoVO();
|
||||
tokenInfoVO.setAccessToken(tokenInfoBO.getAccessToken());
|
||||
tokenInfoVO.setRefreshToken(tokenInfoBO.getRefreshToken());
|
||||
tokenInfoVO.setExpiresIn(tokenInfoBO.getExpiresIn());
|
||||
return tokenInfoVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除当前登录的token
|
||||
* @param accessToken 令牌
|
||||
*/
|
||||
public void deleteCurrentToken(String accessToken) {
|
||||
// 删除用户缓存
|
||||
String keyName = OauthCacheNames.USER_INFO + accessToken;
|
||||
redisTemplate.delete(keyName);
|
||||
// 移除token
|
||||
StpUtil.logoutByTokenValue(accessToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成各系统唯一uid
|
||||
* @param sysType 系统类型
|
||||
* @param userId 用户id
|
||||
* @return
|
||||
*/
|
||||
private String getUid(String sysType, String userId) {
|
||||
return sysType + ":" + userId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
|
||||
package com.pxdj.common.permission;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pxdj.common.util.AuthUserContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* 接口权限判断工具
|
||||
* @author lh
|
||||
*/
|
||||
@Slf4j
|
||||
@Component("pms")
|
||||
public class PermissionService {
|
||||
/**
|
||||
* 判断接口是否有xxx:xxx权限
|
||||
*
|
||||
* @param permission 权限
|
||||
* @return {boolean}
|
||||
*/
|
||||
public boolean hasPermission(String permission) {
|
||||
if (StrUtil.isBlank(permission)) {
|
||||
return false;
|
||||
}
|
||||
return AuthUserContext.get().getPerms()
|
||||
.stream()
|
||||
.filter(StringUtils::hasText)
|
||||
.anyMatch(x -> PatternMatchUtils.simpleMatch(permission, x));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.response;
|
||||
|
||||
/**
|
||||
* @author lanhai
|
||||
*/
|
||||
public interface ResponseCode {
|
||||
|
||||
int SUCCESS = 1;
|
||||
int FAIL = -1;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.response;
|
||||
|
||||
/**
|
||||
* @author FrozenWatermelon
|
||||
* @date 2020/7/9
|
||||
*/
|
||||
public enum ResponseEnum {
|
||||
|
||||
/**
|
||||
* ok
|
||||
*/
|
||||
OK("00000", "ok"),
|
||||
SHOW_FAIL("A00001", "cw"),
|
||||
|
||||
/**
|
||||
* 用于直接显示提示用户的错误,内容由输入内容决定
|
||||
*/
|
||||
|
||||
/**
|
||||
* 用于直接显示提示系统的成功,内容由输入内容决定
|
||||
*/
|
||||
SHOW_SUCCESS("A00002", ""),
|
||||
|
||||
/**
|
||||
* 未授权
|
||||
*/
|
||||
UNAUTHORIZED("A00004", "Unauthorized"),
|
||||
|
||||
/**
|
||||
* 服务器出了点小差
|
||||
*/
|
||||
EXCEPTION("A00005", "服务器出了点小差"),
|
||||
/**
|
||||
* 方法参数没有校验,内容由输入内容决定
|
||||
*/
|
||||
METHOD_ARGUMENT_NOT_VALID("A00014", "方法参数没有校验");
|
||||
|
||||
private final String code;
|
||||
|
||||
private final String msg;
|
||||
|
||||
public String value() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
ResponseEnum(String code, String msg) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResponseEnum{" + "code='" + code + '\'' + ", msg='" + msg + '\'' + "} " + super.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
|
||||
package com.pxdj.common.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author lanhai
|
||||
*/
|
||||
@Data
|
||||
public class ServerResponse<T> implements Serializable {
|
||||
|
||||
|
||||
private int code;
|
||||
|
||||
private String msg;
|
||||
|
||||
private T obj;
|
||||
|
||||
public boolean isSuccess(){
|
||||
return Objects.equals(ResponseCode.SUCCESS, this.code);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
|
||||
package com.pxdj.common.response;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author lanhai
|
||||
*/
|
||||
@Slf4j
|
||||
public class ServerResponseEntity<T> implements Serializable {
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 信息
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* 数据
|
||||
*/
|
||||
private T data;
|
||||
|
||||
/**
|
||||
* 版本
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 时间
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
private String sign;
|
||||
|
||||
public String getSign() {
|
||||
return sign;
|
||||
}
|
||||
|
||||
public void setSign(String sign) {
|
||||
this.sign = sign;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public ServerResponseEntity setData(T data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(Long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return Objects.equals(ResponseEnum.OK.value(), this.code);
|
||||
}
|
||||
public boolean isFail() {
|
||||
return !Objects.equals(ResponseEnum.OK.value(), this.code);
|
||||
}
|
||||
|
||||
public ServerResponseEntity() {
|
||||
// 版本号
|
||||
this.version = "mall4j.v230424";
|
||||
}
|
||||
|
||||
public static <T> ServerResponseEntity<T> success(T data) {
|
||||
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
|
||||
serverResponseEntity.setData(data);
|
||||
serverResponseEntity.setCode(ResponseEnum.OK.value());
|
||||
return serverResponseEntity;
|
||||
}
|
||||
|
||||
public static <T> ServerResponseEntity<T> success() {
|
||||
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
|
||||
serverResponseEntity.setCode(ResponseEnum.OK.value());
|
||||
serverResponseEntity.setMsg(ResponseEnum.OK.getMsg());
|
||||
return serverResponseEntity;
|
||||
}
|
||||
|
||||
public static <T> ServerResponseEntity<T> success(Integer code, T data) {
|
||||
return success(String.valueOf(code), data);
|
||||
}
|
||||
|
||||
public static <T> ServerResponseEntity<T> success(String code, T data) {
|
||||
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
|
||||
serverResponseEntity.setCode(code);
|
||||
serverResponseEntity.setData(data);
|
||||
return serverResponseEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 前端显示失败消息
|
||||
* @param msg 失败消息
|
||||
* @return
|
||||
*/
|
||||
public static <T> ServerResponseEntity<T> showFailMsg(String msg) {
|
||||
log.error(msg);
|
||||
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
|
||||
serverResponseEntity.setMsg(msg);
|
||||
serverResponseEntity.setCode(ResponseEnum.SHOW_FAIL.value());
|
||||
return serverResponseEntity;
|
||||
}
|
||||
|
||||
public static <T> ServerResponseEntity<T> fail(ResponseEnum responseEnum) {
|
||||
log.error(responseEnum.toString());
|
||||
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
|
||||
serverResponseEntity.setMsg(responseEnum.getMsg());
|
||||
serverResponseEntity.setCode(responseEnum.value());
|
||||
return serverResponseEntity;
|
||||
}
|
||||
|
||||
public static <T> ServerResponseEntity<T> fail(ResponseEnum responseEnum, T data) {
|
||||
log.error(responseEnum.toString());
|
||||
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
|
||||
serverResponseEntity.setMsg(responseEnum.getMsg());
|
||||
serverResponseEntity.setCode(responseEnum.value());
|
||||
serverResponseEntity.setData(data);
|
||||
return serverResponseEntity;
|
||||
}
|
||||
|
||||
public static <T> ServerResponseEntity<T> fail(String code, String msg, T data) {
|
||||
log.error(msg);
|
||||
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
|
||||
serverResponseEntity.setMsg(msg);
|
||||
serverResponseEntity.setCode(code);
|
||||
serverResponseEntity.setData(data);
|
||||
return serverResponseEntity;
|
||||
}
|
||||
|
||||
public static <T> ServerResponseEntity<T> fail(String code, String msg) {
|
||||
return fail(code, msg, null);
|
||||
}
|
||||
|
||||
public static <T> ServerResponseEntity<T> fail(Integer code, T data) {
|
||||
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
|
||||
serverResponseEntity.setCode(String.valueOf(code));
|
||||
serverResponseEntity.setData(data);
|
||||
return serverResponseEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ServerResponseEntity{" +
|
||||
"code='" + code + '\'' +
|
||||
", msg='" + msg + '\'' +
|
||||
", data=" + data +
|
||||
", version='" + version + '\'' +
|
||||
", timestamp=" + timestamp +
|
||||
", sign='" + sign + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.util;
|
||||
|
||||
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import com.pxdj.common.bo.UserInfoInTokenBO;
|
||||
|
||||
/**
|
||||
* @author FrozenWatermelon
|
||||
* @date 2020/7/16
|
||||
*/
|
||||
public class AuthUserContext {
|
||||
|
||||
private static final ThreadLocal<UserInfoInTokenBO> USER_INFO_IN_TOKEN_HOLDER = new TransmittableThreadLocal<>();
|
||||
|
||||
public static UserInfoInTokenBO get() {
|
||||
return USER_INFO_IN_TOKEN_HOLDER.get();
|
||||
}
|
||||
|
||||
public static void set(UserInfoInTokenBO userInfoInTokenBo) {
|
||||
USER_INFO_IN_TOKEN_HOLDER.set(userInfoInTokenBo);
|
||||
}
|
||||
|
||||
public static void clean() {
|
||||
if (USER_INFO_IN_TOKEN_HOLDER.get() != null) {
|
||||
USER_INFO_IN_TOKEN_HOLDER.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
||||
*
|
||||
* https://www.mall4j.com/
|
||||
*
|
||||
* 未经允许,不可做商业用途!
|
||||
*
|
||||
* 版权所有,侵权必究!
|
||||
*/
|
||||
package com.pxdj.common.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* token信息,该信息用户返回给前端,前端请求携带accessToken进行用户校验
|
||||
*
|
||||
* @author FrozenWatermelon
|
||||
* @date 2020/7/2
|
||||
*/
|
||||
@Data
|
||||
public class TokenInfoVO {
|
||||
|
||||
@Schema(description = "accessToken" )
|
||||
private String accessToken;
|
||||
|
||||
@Schema(description = "refreshToken" )
|
||||
private String refreshToken;
|
||||
|
||||
@Schema(description = "在多少秒后过期" )
|
||||
private Integer expiresIn;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
com.pxdj.common.adapter.CaptchaCacheServiceRedisImpl
|
||||
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |