亿级高并发电商项目-- 实战篇 --万达商城项目 十二(编写用户服务、发送短信功能、发送注册验证码功能、手机号验证码登录功能、单点登录等模块)
👏作者简介:大家好,我是小童,Java开发工程师,CSDN博客博主,Java领域新星创作者
📕系列专栏:前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶
📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀
🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦
🍂博主正在努力完成2023计划中:以梦为马,扬帆起航,2023追梦人
专栏:高并发项目
编写用户服务接口
在通用模块编写用户服务接口:
/**
* 商城用户服务
*/
public interface ShoppingUserService {// 注册时向redis保存手机号+验证码void saveRegisterCheckCode(String phone,String checkCode);// 注册时验证手机号void registerCheckCode(String phone,String checkCode);// 用户注册void register(ShoppingUser shoppingUser);// 用户名密码登录String loginPassword(String username,String password);// 登录时向redis保存手机号+验证码void saveLoginCheckCode(String phone,String checkCode);// 手机号验证码登录String loginCheckCode(String phone, String checkCode); // 获取登录用户名String getName(String token);// 获取登录用户ShoppingUser getLoginUser(String token);
}
创建网站用户服务模块
1、创建名为 shopping_user_service 的SpringBoot工程,添加相关依赖。
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!-- MyBatisPlus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.0</version></dependency><!-- mysql驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>com.itbaizhan</groupId><artifactId>shopping_common</artifactId><version>0.0.1-SNAPSHOT</version></dependency><!-- dubbo --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.7.8</version></dependency><!-- 操作zookeeper --><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>4.2.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
</dependencies>
2、设置该工程的父工程为 shopping 。
<parent><groupId>com.ittxc</groupId><artifactId>shopping</artifactId><version>1.0-SNAPSHOT</version>
</parent>
3、给 shopping 工程设置子模块
<!-- 子模块 -->
<modules><!-- 用户服务 --><module>shopping_user_service</module>
</modules>
4、编写配置文件 application.yml
# 端口号
server:port: 9006# 日志格式
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n'
#配置mybatis-plus
mybatis-plus:global-config:db-config:# 表名前缀table-prefix: bz_# 主键生成策略为自增id-type: autoconfiguration:# 关闭列名自动驼峰命名规则映射map-underscore-to-camel-case: falselog-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启sql日志
spring:# 数据源datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql:///baizhanshopping?serverTimezone=UTCusername: rootpassword01: 123456# redisredis:host: 192.168.100.131port: 6379timeout: 30000jedis:pool:max-active: 8max-wait: -1max-idle: 8min-idle: 0
dubbo:application:name: shopping_user_service # 项目名registry:address: zookeeper://192.168.100.131 #注册中心地址port: 2181 # 注册中心的端口timeout: 10000 # 注册到zk上超时时间,msprotocol:name: dubbo # dubbo使用的协议port: -1 # dubbo自动分配端口scan:base-packages: com.ittxc.shopping_user_service.service # 包扫描
创建网站用户Api模块
1、创建名为 shopping_user_customer_api 的SpringBoot工程,添加相关依赖。
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- dubbo --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.7.8</version></dependency><!-- 操作zookeeper --><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>4.2.0</version></dependency><dependency><groupId>com.itbaizhan</groupId><artifactId>shopping_common</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>
2、设置该工程的父工程为 shopping 。
<parent><groupId>com.ittxc</groupId><artifactId>shopping</artifactId><version>1.0-SNAPSHOT</version>
</parent>
3、给 shopping 工程设置子模块
<!-- 子模块 -->
<modules><!-- 用户业务的api --><module>shopping_user_customer_api</module>
</modules>
4、编写配置文件 application.yml
# 端口号
server:port: 8003
# 日志格式
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n'
dubbo:application:name: shopping_user_customer_api # 项目名registry:address: zookeeper://192.168.100.131 #注册中心地址port: 2181 # 注册中心的端口timeout: 10000 # 注册到zk上超时时间,msprotocol:name: dubbo # dubbo使用的协议port: -1 # dubbo自动分配端口
5、启动类忽略数据源自动配置
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
public class ShoppingUserCustomerApiApplication {public static void main(String[] args){SpringApplication.run(ShoppingUserCustomerApiApplication.class, args);}
}
用户注册的步骤
在用户注册时,我们要保证用户输入的手机号就是他本人使用的手 机号,方便后期的广告推送、安全认证等。所以在注册前,会向用 户手机发送一个四位随机数验证码,如果用户能获取到该验证码, 证明该手机就是用户本人使用。用户注册的步骤如下:
申请阿里短信服务
1、访问阿里云 https://www.aliyun.com/,完成登录
2、进入短信服务
3、开通短信服务
4、购买短信条数
5、购买完成进入阿里云短信控制台https://dysms.console.aliyun.c om/overview
6、绑定测试手机号
7、点击调用API发送短信,可以看到发送短信的JAVA代码。
8、申请阿里云秘钥,该秘钥在发送短信时会作为参数传入
编写发送短信功能
1、创建名为 shopping_message_service 的SpringBoot工程,添加相关依赖。
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency>
<dependency><groupId>com.ittxc</groupId><artifactId>shopping_common</artifactId><version>0.0.1-SNAPSHOT</version></dependency><!-- 阿里短信平台 --><dependency><groupId>com.aliyun</groupId><artifactId>dysmsapi20170525</artifactId><version>2.0.9</version></dependency><!-- dubbo --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.7.8</version></dependency><!-- 操作zookeeper --><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>4.2.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>
2、设置该工程的父工程为 shopping 。
<parent><groupId>com.ittxc</groupId><artifactId>shopping</artifactId><version>1.0-SNAPSHOT</version>
</parent>
3、给 shopping 工程设置子模块
<!-- 子模块 -->
<modules><!-- 短信服务 --><module>shopping_message_service</module>
</modules>
4、编写配置文件 application.yml
# 端口号
server:port: 9007
# 日志格式
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread]
%cyan(%-50logger{50}):%msg%n'
dubbo:application:name: shopping_message_service # 项目名registry:address: zookeeper://192.168.100.131 #注册中心地址port: 2181 # 注册中心的端口timeout: 10000 # 注册到zk上超时时间,msprotocol:name: dubbo # dubbo使用的协议port: -1 # dubbo自动分配端口scan:base-packages: com.ittxc.shopping_message_service.service # 包扫描
message:accessKeyId: LTAI5tBUnRMTgKmRR92yxrFfaccessKeySecret: RN7umfy3mMA2xlGa8rP0IzwQdTc6Pb
5、启动类忽略数据源自动配置
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
public class ShoppingMessageServiceApplication {public static void main(String[] args){SpringApplication.run(ShoppingMessageServiceApplication.class, args);}
}
6、在通用模块编写发送短信服务接口
/**
* 短信服务
*/
public interface MessageService {/*** 发送短信* @param phoneNumber 手机号* @param code 验证码* @return 返回结果*/BaseResult sendMessage(String phoneNumber,String code);
}
7、在短信服务模块编写发送短信实现类
@DubboService
public class MessageServiceImpl implements
MessageService {@Value("${message.accessKeyId}")private String accessKeyId;@Value("${message.accessKeySecret}")private String accessKeySecret;/*** 使用AK&SK初始化账号Client* @param accessKeyId* @param accessKeySecret* @return Client* @throws Exception*/@SneakyThrowsprivate Client createClient(String accessKeyId, String accessKeySecret) {Config config = new Config().setAccessKeyId(accessKeyId).setAccessKeySecret(accessKeySecret);// 访问的域名config.endpoint = "dysmsapi.aliyuncs.com";return new Client(config);}@SneakyThrows@Overridepublic BaseResult sendMessage(String phoneNumber,String code) {Client client = createClient(accessKeyId,accessKeySecret);SendSmsRequest sendSmsRequest = new SendSmsRequest().setSignName("阿里云短信测试").setTemplateCode("SMS_154950909").setPhoneNumbers(phoneNumber).setTemplateParam("{\"code\":\""+code+"\"}");RuntimeOptions runtime = new RuntimeOptions();// 复制代码运行请自行打印API的返回值SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest,
runtime);SendSmsResponseBody body = sendSmsResponse.getBody();if ("OK".equals(body.getCode())){return new BaseResult(200,body.getCode(),body.getMessage());}else{return new BaseResult(500,body.getCode(),body.getMessage());}}
}
8、测试该方法
编写发送注册验证码功能
1、在通用模块编写生成随机数的工具类
public class RandomUtil {/*** 生成验证码* @param digit 位数* @return*/public static String buildCheckCode(int digit){String str = "0123456789";StringBuilder sb = new StringBuilder();Random random = new Random();for (int i = 0; i < digit; i++) {char ch = str.charAt(random.nextInt(str.length()));sb.append(ch);}return sb.toString();}
}
2、在用户服务模块编写用户服务接口实现类
@DubboService
public class ShoppingUserServiceImpl implements ShoppingUserService {@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate ShoppingUserMapper shoppingUserMapper;@Overridepublic void saveRegisterCheckCode(String phone, String checkCode) {ValueOperations valueOperations = redisTemplate.opsForValue();// redis键为手机号,值为验证码,过期时间5分钟valueOperations.set("registerCode:" + phone, checkCode, 300, TimeUnit.SECONDS);}
}
3、在用户API模块编写控制器
/**
* 商城用户
*/
@RestController
@RequestMapping("/shoppingUser")
public class ShoppingUserController {@DubboReferenceprivate ShoppingUserService shoppingUserService;/*** 发送注册短信* @param phone 注册手机号* @return 操作结果*/@GetMapping("/sendMessage")public BaseResult sendMessage(String phone){// 1.生成随机四位数String checkCode = RandomUtil.buildCheckCode(4);// 2.发送短信BaseResult result = messageService.sendMessage(phone,checkCode);// 3.发送成功,将验证码保存到redis中,发送失败,返回发送结果if (200 == result.getCode()) {shoppingUserService.saveRegisterCheckCode(phone, checkCode);return BaseResult.ok();} else {return result;}}
}
4、测试控制器
编写验证注册验证码功能
1、在用户服务模块编写用户服务接口实现类
@Override
public void registerCheckCode(String phone, String checkCode) {// 验证验证码ValueOperations valueOperations = redisTemplate.opsForValue();Object checkCodeRedis = valueOperations.get("registerCode:" + phone);if (!checkCode.equals(checkCodeRedis)){throw new BusException(CodeEnum.REGISTER_CODE_ERROR);}
}
2、在用户API模块编写控制器
/*** 验证用户注册验证码* @param phone 手机号* @param checkCode 验证码* @return 200验证成功,605验证码不正确*/
@GetMapping("/registerCheckCode")
public BaseResult register(String phone,String checkCode){shoppingUserService.registerCheckCode(phone,checkCode);return BaseResult.ok();
}
3、测试控制器
编写用户注册功能
1、在通用模块编写MD5加密工具类,用于给用户密码进行加密和验证
public class Md5Util {public final static String md5key = "BAIZHAN"; // 秘钥/*** 加密* @param text 明文* @return 密文*/public static String encode(String text){return DigestUtils.md5Hex(text + md5key);}/*** 验证** @param text 明文* @param cipher 密文* @return true/false*/public static boolean verify(String text, String cipher){// 将明文转为密文进行比对String md5Text = encode(text);if (md5Text.equalsIgnoreCase(cipher)) {return true;}return false;}
}
2、在用户服务模块编写用户服务接口实现类
@Override
public void register(ShoppingUser
shoppingUser) {// 1.验证手机号是否存在String phone = shoppingUser.getPhone();QueryWrapper<ShoppingUser> queryWrapper = new QueryWrapper();queryWrapper.eq("phone", phone);List<ShoppingUser> shoppingUsers = shoppingUserMapper.selectList(queryWrapper);if (shoppingUsers != null && shoppingUsers.size() > 0) {throw new BusException(CodeEnum.REGISTER_REPEAT_PHONE_ERROR);}// 2.验证用户名是否存在String username = shoppingUser.getUsername();QueryWrapper<ShoppingUser> queryWrapper1 = new QueryWrapper();queryWrapper1.eq("username",username);List<ShoppingUser> shoppingUsers1 = shoppingUserMapper.selectList(queryWrapper1);if (shoppingUsers1 != null && shoppingUsers1.size() > 0) {throw new BusException(CodeEnum.REGISTER_REPEAT_NAME_ERROR);}// 3.新增用户shoppingUser.setStatus("Y");shoppingUser.setPassword(Md5Util.encode(shoppingUser.getPassword()));shoppingUserMapper.insert(shoppingUser);
}
3、在用户API模块编写控制器
/*** 用户注册* @param shoppingUser 用户信息* @return 注册结果*/
@PostMapping("/register")
public BaseResult register(@RequestBody ShoppingUser shoppingUser){shoppingUserService.register(shoppingUser);return BaseResult.ok();
}
4、测试控制器
编写用户名密码登录功能
1、在用户服务模块编写用户服务接口实现类
@Override
public String loginPassword(String username, String password) {QueryWrapper<ShoppingUser> queryWrapper = new QueryWrapper();queryWrapper.eq("username", username);ShoppingUser shoppingUser = shoppingUserMapper.selectOne(queryWrapper);// 验证用户名if (shoppingUser == null) {throw new BusException(CodeEnum.LOGIN_NAME_PASSWORD_ERROR);}// 验证密码boolean verify = Md5Util.verify(password,shoppingUser.getPassword());if (!verify) {throw new BusException(CodeEnum.LOGIN_NAME_PASSWORD_ERROR);}// 返回用户名return username;
}
2、在用户API模块编写控制器
/*** 用户名密码登录* @param shoppingUser 用户对象* @return 登录结果*/
@PostMapping("/loginPassword")
public BaseResult loginPassword(@RequestBody ShoppingUser shoppingUser){shoppingUserService.loginPassword(shoppingUser.getUsername(),shoppingUser.getPassword());return BaseResult.ok();
}
3、测试控制器
编写手机号验证码登录功能
手机号验证码登录的流程为:用户先输入手机号,向手机号发送随 机验证码,并将验证码保存到redis中。用户收到短信后将验证码输入,如果验证码和redis中的验证码匹配成功,证明登录者就是使用 该手机的用户,登录成功。
向用户发送登录验证码
1、在用户服务模块编写用户服务接口实现类
// 保存登录验证码到redis
@Override
public void saveLoginCheckCode(String phone, String checkCode) {ValueOperations valueOperations = redisTemplate.opsForValue();// redis键为手机号,值为验证码,过期时间5分钟valueOperations.set("loginCode:" + phone, checkCode, 300, TimeUnit.SECONDS);
}
2、在用户API模块编写控制器
/*** 发送登录短信验证码** @param phone 手机号* @return 操作结果*/
@GetMapping("/sendLoginCheckCode")
public BaseResult sendLoginCheckCode(String phone) {// 1.生成随机四位数String checkCode = RandomUtil.buildCheckCode(4);// 2.发送短信BaseResult result = messageService.sendMessage(phone,checkCode);// 3.发送成功,将验证码保存到redis中,发送失败,返回发送结果if (200 == result.getCode()) {shoppingUserService.saveLoginCheckCode(phone, checkCode);return BaseResult.ok();} else {return result;}
}
3、测试控制器
验证登录验证码
1、在用户服务模块编写用户服务接口实现类
// 验证登录验证码
@Override
public String loginCheckCode(String phone, String checkCode) {ValueOperations valueOperations = redisTemplate.opsForValue();Object checkCodeRedis = valueOperations.get("loginCode:" + phone);if (!checkCode.equals(checkCodeRedis)){throw new BusException(CodeEnum.LOGIN_CODE_ERROR);}// 登录成功,查询用户QueryWrapper<ShoppingUser> queryWrapper = new QueryWrapper();queryWrapper.eq("phone", phone);ShoppingUser shoppingUser = shoppingUserMapper.selectOne(queryWrapper);// 返回用户名return shoppingUser.getUsername();
}
2、在用户API模块编写控制器
/*** 手机号验证码登录* @param phone 手机号* @param checkCode 验证码* @return 登录结果*/
@PostMapping("/loginCheckCode")
public BaseResult loginCheckCode(String phone, String checkCode){shoppingUserService.loginCheckCode(phone,checkCode);return BaseResult.ok();
}
3、测试控制器
单点登录的概念
目前登录成功后我们并没有保存用户信息,也没有在访问接口前验 证用户信息。如果使用传统的session保存用户信息,使用filter验证 用户是否登录。存在一个问题:session保存在服务器中,而我们的 系统存在诸多子系统,这些子系统是分别部署在不同的服务器中,互相无法访问(登录后的session保存在用户模块,搜索模块无法访问用户模块的session,无法验证用户是否登录)。此时我们需要使用单点登录技术解决这一问题。
单点登录
单点登录(Single Sign On)简称为 SSO。即在多个应用系统中, 用户只需要登录一次就可以访问所有相互信任的应用系统。JWT是一种常用的单点登录解决方案。
JWT
JWT是Json Web Token的简称,是一种令牌生成算法。使用JWT能 够保证Token的安全性,且能够进行Token时效性的检验。 使用JWT时,登录成功后将用户信息生成一串令牌字符串。将该字 符串返回给客户端,客户端每次请求时都在请求头携带该令牌字符串。在其他模块验证令牌,通过则证明用户处于登录状态,并拿到 解析后的用户信息,未通过证明用户处于未登录状态。
编写单点登录功能
1、在通用模块引入JWT依赖
<!-- JWT -->
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.4.0</version>
</dependency>
2、在通用模块编写JWT工具类
public class JWTUtil {//token过期时间,一天private static final Long EXPIRE_DATE = 1000*60*60*24L;// 秘钥private static final String SECRET = "txc";// 签发者private static final String ISSUER = "TXC";/*** 签名生成* @param shoppingUser* @return*/public static String sign(ShoppingUser shoppingUser){String token = JWT.create().withIssuer(ISSUER) // 签发者.withIssuedAt(new Date())// 签发时间.withExpiresAt(new Date(new Date().getTime() + EXPIRE_DATE))// 过期时间.withSubject(shoppingUser.getUsername())// 保存用户名.sign(Algorithm.HMAC256(SECRET)); // 秘钥return token;}/*** 签名解析* @param token 签名字符串* @return 解析得出的用户名*/public static String verify(String token){try {String username = JWT.require(Algorithm.HMAC256(SECRET)).withIssuer(ISSUER).build().verify(token).getSubject();return username;} catch (Exception e){throw new BusException(CodeEnum.VERIFY_TOKEN_ERROR);}}
}
3、登录后生成令牌
@Override
public String loginPassword(String
username, String password) {// 1.验证用户名QueryWrapper<ShoppingUser> queryWrapper = new QueryWrapper();queryWrapper.eq("username",username);ShoppingUser shoppingUser = shoppingUserMapper.selectOne(queryWrapper);if (shoppingUser == null){throw new BusException(CodeEnum.LOGIN_NAME_PASSWORD_ERROR);}// 2.验证密码boolean verify = Md5Util.verify(password, shoppingUser.getPassword());if (!verify){throw new BusException(CodeEnum.LOGIN_NAME_PASSWORD_ERROR);}// 3.生成JWT令牌,返回令牌String sign = JWTUtil.sign(shoppingUser);return sign;
}
@Override
public String loginCheckCode(String phone,String checkCode) {ValueOperations valueOperations = redisTemplate.opsForValue();Object checkCodeRedis = valueOperations.get("loginCode:" + phone);if (!checkCode.equals(checkCodeRedis)){throw new BusException(CodeEnum.LOGIN_CODE_ERROR);}// 登录成功,查找用户,返回用户名QueryWrapper<ShoppingUser> queryWrapper = new QueryWrapper();queryWrapper.eq("phone",phone);ShoppingUser shoppingUser = shoppingUserMapper.selectOne(queryWrapper);// 生成JWT令牌,返回令牌String sign = JWTUtil.sign(shoppingUser);return sign;
}
4、将令牌返回给客户端
/*** 用户名密码登录* @param shoppingUser 用户对象* @return 登录结果*/
@PostMapping("/loginPassword")
public BaseResult loginPassword(@RequestBody ShoppingUser shoppingUser){String sign = shoppingUserService.loginPassword(shoppingUser.getUsername(),shoppingUser.getPassword());return BaseResult.ok(sign);
}
/**
* 手机号验证码登录
* @param phone 手机号
* @param checkCode 验证码
* @return 登录结果
*/
@PostMapping("/loginCheckCode")
public BaseResult loginCheckCode(Stringphone,String checkCode){String sign = shoppingUserService.loginCheckCode(phone,checkCode);return BaseResult.ok(sign);
}
编写拦截器验证令牌
在通用模块编写JWT拦截器
// 拦截器,验证令牌
public class JWTInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response, Objecthandler) throws Exception {// 获取请求头中的tokenString token = request.getHeader("token");// 验证tokenJWTUtil.verify(token);return true;}
}
注:请求时要在请求头添加token=令牌数据
配置拦截的接口
在不同API模块都要配置拦截的接口
1、配置用户API模块拦截的接口
// 拦截器配置
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new JWTInterceptor()).addPathPatterns("/**") //拦截的接口.excludePathPatterns("/user/shoppingUser/sendMessage","/user/shoppingUser/registerCheckCode","/user/shoppingUser/register","/user/shoppingUser/loginPassword","/user/shoppingUser/sendLoginCheckCode","/user/shoppingUser/loginCheckCode"); //放行的接口}
}
2、配置搜索API模块拦截的接口
// 拦截器配置
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new JWTInterceptor()).addPathPatterns("/**") //拦截的接口.excludePathPatterns("/user/goodsSearch/autoSuggest"); //放行的接口}
}
3、广告用户API模块不拦截
编写获取用户名功能
1、在用户服务模块编写用户服务接口实现类
@Override
public String getName(String token) {String name = JWTUtil.verify(token);return name;
}
@Override
public ShoppingUser getLoginUser(String token) {String username = JWTUtil.verify(token);QueryWrapper<ShoppingUser> queryWrapper = new QueryWrapper();queryWrapper.eq("username", username);ShoppingUser shoppingUser = shoppingUserMapper.selectOne(queryWrapper);return shoppingUser;
}
2、在用户API模块编写控制器
/*** 获取登录的用户名* @param token 令牌* @return 用户名*/
@GetMapping("/getName")
public BaseResult<String> getName(@RequestHeader("token") String token){String name = shoppingUserService.getName(token);return BaseResult.ok(name);
}
3、使用Postman测试控制器
编写退出登录功能
后端无需编写退出登录功能,前端删除令牌即可退出登录。
相关文章:

亿级高并发电商项目-- 实战篇 --万达商城项目 十二(编写用户服务、发送短信功能、发送注册验证码功能、手机号验证码登录功能、单点登录等模块)
👏作者简介:大家好,我是小童,Java开发工程师,CSDN博客博主,Java领域新星创作者 📕系列专栏:前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶 Ǵ…...

整合spring cloud云服务架构 - 企业分布式微服务云架构构建
1. 介绍 Commonservice-system是一个大型分布式、微服务、面向企业的JavaEE体系快速研发平台,基于模块化、服务化、原子化、热插拔的设计思想,使用成熟领先的无商业限制的主流开源技术构建。采用服务化的组件开发模式,可实现复杂的业务功能。…...

leetcode 540. Single Element in a Sorted Array(排序数组中的单个元素)
给一个已经排好序的升序数组,其中每个元素都会重复2次,只有一个元素只有一个, 找出这个只有一个的元素。 要求时间复杂度在O(logn), 空间复杂度在O(1). 思路: 时间复杂度为O(logn), 让人想到了binary search. 因为时间复杂度为…...

Color correction for tone mapping
Abstract色调映射算法提供了复杂的方法,将真实世界的亮度范围映射到输出介质的亮度范围,但它们经常导致颜色外观的变化。在本研究中,我们进行了一系列的主观外观匹配实验,以测量对比度压缩和增强后图像色彩的变化。结果表明&#…...

JavaScript-XHR-深入理解
JavaScript-XHR-深入理解1. XHR(Asynchronous JavaScript And XML)初始1.1. xhr request demo1.2. status of XHRHttpRequest1.3. send synchronous request by xhr1.4. onload监听数据加载完成1.5. http status code1.6. get/post request with josn/form/urlcoded1.7. encaps…...

mathtype7.0最新版安装下载及使用教程
MathType是一款专业的数学公式编辑器,理科生专用的必备工具,可应用于教育教学、科研机构、工程学、论文写作、期刊排版、编辑理科试卷等领域。2014年11月,Design Science将MathType升级到MathType 6.9版本。在苏州苏杰思网络有限公司与Design…...
响应状态码
✨作者:猫十二懿 ❤️🔥账号:CSDN 、掘金 、个人博客 、Github 🎉公众号:猫十二懿 一、状态码大类 状态码分类说明1xx响应中——临时状态码,表示请求已经接受,告诉客户端应该继续请求或者如果…...

第六章.卷积神经网络(CNN)—CNN的实现(搭建手写数字识别的CNN)
第六章.卷积神经网络(CNN) 6.2 CNN的实现(搭建手写数字识别的CNN) 1.网络构成 2.代码实现 import pickle import matplotlib.pyplot as plt import numpy as np import sys, ossys.path.append(os.pardir)from dataset.mnist import load_mnist from collections import Order…...
【go】defer底层原理
defer的作用 defer声明的函数在当前函数return之后执行,通常用来做资源、连接的关闭和缓存的清除等。 A defer statement pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns. Defer is commonly u…...

TypeScript 学习笔记
最近在学 ts 顺便记录一下自己的学习进度,以及一些知识点的记录,可能不会太详细,主要是用来巩固和复习的,会持续更新 前言 想法 首先我自己想说一下自己在学ts之前,对ts的一个想法和印象,在我学习之前&a…...

【C++】map和set的使用
map和set一、set1.1 set的介绍1.2 set的使用1.2.1 set的构造1.2.2 set的迭代器1.2.3 set的修改1.2.3.1 insert && find && erase1.2.3.2 count1.3 multiset二、map2.1 map的介绍2.2 map的使用2.2.1 map的修改2.2.1.1 insert2.2.1.2 统计次数2.3 multimap一、se…...

微电影广告具有哪些特点?
微电影广告是广告主投资的,以微电影为形式载体,以新媒体为主要传播载体,综合运用影视创作手法拍摄的集故事性、艺术性和商业性于一体的广告。它凭借精彩的电影语言和强大的明星效应多渠道联动传播,润物细无声地渗透和传递着商品信…...

Android RxJava框架源码解析(四)
目录一、观察者Observer创建过程二、被观察者Observable创建过程三、subscribe订阅过程四、map操作符五、线程切换原理简单示例1: private Disposable mDisposable; Observable.create(new ObservableOnSubscribe<String>() {Overridepublic void subscribe(…...
Linux信号-进程退出状态码
当进程因收到信号被终止执行退出后,父进程可以通过wait或waitpid得到它的exit code。进程被各信号终止的退出状态码总结如下:信号编号信号名称信号描述默认处理方式Exit code1SIGHUP挂起终止12SIGINT终端中断终止23SIGQUIT终端退出终止、coredump1314SIG…...

springcloud+vue实现图书管理系统
一、前言: 今天我们来分享一下一个简单的图书管理系统 我们知道图书馆系统可以有两个系统,一个是管理员管理图书的系统,管理员可以(1)查找某一本图书情况、(2)增加新的图书、(3&…...

GEE学习笔记 六十:GEE中生成GIF动画
生成GIF动画这个是GEE新增加的功能之一,这一篇文章我会简单介绍一下如何使用GEE来制作GIF动画。 相关API如下: 参数含义: params:设置GIF动画显示参数,详细的参数可以参考ee.data.getMapId() callback:回调…...
react中的useEffect
是函数组件中执行的副作用,副作用就是指每次组件更新都会执行的函数,可以用来取代生命周期。 1. 基本用法 import { useEffect } from "react"; useEffect(()>{console.log(副作用); });2. 副作用分为需要清除的和不需要清除 假如设置…...
故障安全(Crash-Safe) 复制
二进制日志记录是故障安全的:MySQL 仅记录完成的事件或事务使用 sync-binlog 提高安全性默认值是1,最安全的,操作系统在每次事务后写入文件将svnc-binloq 设置为0,当操作系统根据其内部规则写入文件的同时服务器崩溃时性能最好但事务丢失的可…...

Spring aop之针对注解
前言 接触过Spring的都知道,aop是其中重要的特性之一。笔者在开发做项目中,aop更多地是要和注解搭配:在某些方法上加上自定义注解,然后要对这些方法进行增强(很少用execution指定,哪些包下的哪些方法要增强)。那这时就…...

【JavaScript速成之路】JavaScript数据类型转换
📃个人主页:「小杨」的csdn博客 🔥系列专栏:【JavaScript速成之路】 🐳希望大家多多支持🥰一起进步呀! 文章目录前言数据类型转换1,转换为字符串型1.1,利用“”拼接转换成…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...

什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...