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

21-绑定自定义事件
绑定自定义事件 利用自定义事件获取子组件的值 父组件给子组件绑定一个自定义事件,实际上是绑定到了子组件的实例对象vc上: <!-- 自定义myEvent事件 --> <Student v-on:myEventgetStudentName/>在父组件中编写getStudentName的实现&#…...

【Mysql】触发器
【Mysql】触发器 文章目录【Mysql】触发器1. 触发器1.1 介绍1.2 语法1.2.1 创建触发器1.2.2 查看触发器1.2.3 删除触发器1.2.4 案例1. 触发器 1.1 介绍 触发器是与表有关的数据库对象,指在insert、update、delete之前(BEFORE)或之后(AFTER),触发并执行…...

CODESYS开发教程11-库管理器
今天继续我们的小白教程,老鸟就不要在这浪费时间了😊。 前面一期我们介绍了CODESYS的文件读写函数库SysFile。大家可能发现了,在CODESYS的开发中实际上是离不开各种库的使用,其中包括系统库、第三方库以及用户自己开发的库。实际…...

【UnityAR相关】Unity Vuforia扫图片成模型具体步骤
1 资产准备 导入要生成的fbx模型(带有材质), 你会发现导入fbx的材质丢失了: 选择Standard再Extract Materials导出材质到指定文件夹下(我放在Assets->Materials了 ok啦! 材质出现了, 模型…...

2023年全国最新保安员精选真题及答案2
百分百题库提供保安员考试试题、保安职业资格考试预测题、保安员考试真题、保安职业资格证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 21.一般来说,最经济的巡逻方式是()。 A:步巡 B:…...

keil5安装了pack包但是还是不能选择device
一开始,我以为是keil5无法安装 STM32 芯片包,打开device倒是可以看到stm公司的芯片包,但是没有我想要的stm32f1。 我按照网上的一些说法,找到了这个STM32F1 的pack芯片包,但是我双击安装的时候,它的安装位…...

秒杀系统设计
1.秒杀系统的特点 瞬时高并发 2.预防措施 2.1.流量限制 对于一个相同的用户,限制请求的频次对于一个相同的IP,限制请求的频次验证码,减缓用户请求的次数活动开启之前,按钮先置灰,防止无效的请求流入系统࿰…...

全面认识数据指标体系
什么是数据指标体系? 看了下百度百科,竟然没有数据指标这个词条,看来这个词大家平时还用的不多啊。那只有间接偷懒一下,分别查下指标和数据这两个词条的含义,在组合起来看看。 数据:数据是指对客观事件进…...

热榜首推!阿里内部都在用的Java后端面试笔记,主流技术全在里面了!备战2023Java面试,拿理想offer
纵观今年的技术招聘市场, Java依旧是当仁不让的霸主 !即便遭受 Go等新兴语言不断冲击,依旧岿然不动。究其原因:Java有着极其成熟的生态,这个不用我多说;Java在 运维、可观测性、可监 控性方面都有着非常优秀…...

Android架构设计——【 APT技术实现butterknife框架 】
APT简介 APT英文全称:Android annotation process tool是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文…...