亿级高并发电商项目-- 实战篇 --万达商城项目 十二(编写用户服务、发送短信功能、发送注册验证码功能、手机号验证码登录功能、单点登录等模块)
👏作者简介:大家好,我是小童,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,利用“”拼接转换成…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...

android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

Sklearn 机器学习 缺失值处理 获取填充失值的统计值
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...
WEB3全栈开发——面试专业技能点P4数据库
一、mysql2 原生驱动及其连接机制 概念介绍 mysql2 是 Node.js 环境中广泛使用的 MySQL 客户端库,基于 mysql 库改进而来,具有更好的性能、Promise 支持、流式查询、二进制数据处理能力等。 主要特点: 支持 Promise / async-await…...

高保真组件库:开关
一:制作关状态 拖入一个矩形作为关闭的底色:44 x 22,填充灰色CCCCCC,圆角23,边框宽度0,文本为”关“,右对齐,边距2,2,6,2,文本颜色白色FFFFFF。 拖拽一个椭圆,尺寸18 x 18,边框为0。3. 全选转为动态面板状态1命名为”关“。 二:制作开状态 复制关状态并命名为”开…...
【Java】Ajax 技术详解
文章目录 1. Filter 过滤器1.1 Filter 概述1.2 Filter 快速入门开发步骤:1.3 Filter 执行流程1.4 Filter 拦截路径配置1.5 过滤器链2. Listener 监听器2.1 Listener 概述2.2 ServletContextListener3. Ajax 技术3.1 Ajax 概述3.2 Ajax 快速入门服务端实现:客户端实现:4. Axi…...