> 文章列表 > 交友项目【手机号登录注册功能】实现

交友项目【手机号登录注册功能】实现

交友项目【手机号登录注册功能】实现

目录

1:用户登录

1.1:接口文档

1.2:API接口定义

1.3:Dubbo服务提供者

配置文件

启动引导类

数据访问层

API接口实现

1.4:Dubbo服务消费者

UserController

UserService

1.5:访问测试

1.6:可能存在的问题

序列化异常

连接超时

没有服务提供者可用

2:登录涉及到JWT生成Token

2.1:简介

2.2:格式

2.3:流程

2.4:示例

2.5:JWT工具类


1:用户登录

用户接收到验证码后,进行输入验证码,点击登录,前端系统将手机号以及验证码提交到服务端进行校验。

 

1.1:接口文档

YAPI接口地址:http://192.168.136.160:3000/project/19/interface/api/97  

1.2:API接口定义

在接口模块(tanhua-dubbo-interface),将提供者模块(tanhua-dubbo-db)提供的接口抽取到一个专门的接口模块,便于统一管理,消费者模块只需要导入接口模块的依赖即可调用该接口。

 

/*** @Author 爱吃豆的土豆、* @Date 2023/3/30 11:05*/
public interface UserApi {public User findByMobile(String phone);public Long save(User user);
}

1.3:Dubbo服务提供者

  • tanhua-dubbo-db项目中提供UserApiImpl的实现 , 如下所示

     

配置文件

server:port: 18081
spring:application:name: tanhua-dubbo-dbdatasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/tanhua?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=falseusername: rootpassword: 1234cloud:nacos:discovery:server-addr: 192.168.136.160:8848
dubbo:protocol:name: dubboport: 20881registry:address: spring-cloud://192.168.136.160:8848scan:base-packages: com.czxy.tanhua.dubbo.api  #dubbo中包扫描mybatis-plus:global-config:db-config:table-prefix: tb_   # 表名前缀id-type: auto   # id策略为自增长

启动引导类

/*** @Author 爱吃豆的土豆、* @Date 2023/3/30 14:48*/
@SpringBootApplication
@EnableDubbo
public class DubboDBApplication {public static void main(String[] args) {SpringApplication.run(DubboDBApplication.class,args);}
}

数据访问层

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

API接口实现

@DubboService
public class UserApiImpl  implements UserApi {@Autowiredprivate UserMapper userMapper;//根据手机号码查询用户public User findByMobile(String mobile) {QueryWrapper<User> qw = new QueryWrapper<>();qw.eq("mobile",mobile);return userMapper.selectOne(qw);}@Overridepublic Long save(User user) {userMapper.insert(user);return user.getId();}
}

1.4:Dubbo服务消费者

tanhua-app-server项目中接收APP请求, 调用Dubbo服务完成业务功能

UserController

/*** 校验登录*/
@PostMapping("/loginVerification")
public ResponseEntity loginVerification(@RequestBody Map map) {//1 调用map集合获取请求参数String phone = (String) map.get("phone");String code = (String) map.get("verificationCode");//2 调用userService完成用户登录Map retMap = userService.loginVerification(phone, code);//3 构造返回return ResponseEntity.ok(retMap);
}

UserService

package com.czxy.tanhua.service;import com.czxy.tanhua.autoconfig.template.SmsTemplate;
import com.czxy.tanhua.commons.utils.JwtUtils;
import com.czxy.tanhua.dubbo.api.UserApi;
import com.czxy.tanhua.entity.User;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;/*** @Author 爱吃豆的土豆、* @Date 2023/3/30 9:55*/
@Service
public class UserService {@Resourceprivate SmsTemplate smsTemplate;@Resourceprivate StringRedisTemplate stringRedisTemplate;@DubboReferenceprivate UserApi userApi;/*** 登录用户的验证码* @param phone*/public void loginUser(String phone) {//随机生成验证码String code = RandomStringUtils.randomNumeric(6);//调用短信工具类smsTemplate.sendSms(phone,"",code);String key = "CHECK_CODE"+phone;//存放到redis中stringRedisTemplate.opsForValue().set(key,code,Duration.ofMinutes(5));}/*** 登录验证码的校验&用户注册* @param map* @return*/public Map loginVerification(Map map) {String phone = (String) map.get("phone");String verificationCode = (String) map.get("verificationCode");//将验证码进行校验String s = stringRedisTemplate.opsForValue().get("CHECK_CODE" + phone);//取出之后删除验证码stringRedisTemplate.delete("CHECK_CODE"+phone);if (StringUtils.isEmpty(s) || !s.equals(verificationCode)){throw new RuntimeException("验证码校验失败!");}//判断用户是否已经注册Boolean isNew = false;//验证码校验成功,查询数据库是否存在该用户User user = userApi.findByMobile(phone);if (user == null){//新注册用户user = new User();user.setMobile(phone);user.setPassword(DigestUtils.md5Hex("123456"));//进行添加到数据库中Long id = userApi.save(user);user.setId(id);isNew = true;}//号码已经再数据库中存在生成token进行返回Map<String, Object> tokenMap = new HashMap<>();tokenMap.put("id",user.getId());tokenMap.put("mobile", user.getMobile());String token = JwtUtils.getToken(tokenMap);Map<String, Object> result = new HashMap<>();result.put("token",token);result.put("isNew",isNew);return result;}
}

1.5:访问测试

【第一步】查看ip地址黑窗口ipconfig

【第二步】修改服务配置:【自己电脑的IP地址】  

 【第三步】测试

 

1.6:可能存在的问题

序列化异常

 

连接超时

Nacos连接超时 ,Redis连接超时等

等待很长时间, 报如下类似的错误

 

找到连接超时的服务 :

  1. 查看服务是否启动

  2. 查看服务连接配置IP端口是否正确

  3. 查看开发机器和服务所在机器网络是否通畅(ping指令)

没有服务提供者可用

没有服务提供者可用

  1. 检查服务提供者工程是否开启成功

  2. 检查注册中心是否存在该服务

  3. 注册中心不存在 服务

    1. 检查dubbo扫描的包是否正确

    2. 检查@DubboService注解是否添加

  4. 注册中心存在服务

    1. 检查服务消费方的注册中心地址是否正确

    2. 检查提供方和消费方的接口定义是否相同

 

2:登录涉及到JWT生成Token

2.1:简介

  • JSON Web token简称JWT, 是用于对应用程序上的用户进行身份验证的标记。也就是说, 使用 JWTS 的应用程序不再需要保存有关其用户的 cookie 或其他session数据。此特性便于可伸缩性, 同时保证应用程序的安全

2.2:格式

  • JWT就是一个字符串,经过加密处理与校验处理的字符串,形式为:A.B.C

  • A由JWT头部信息header加密得到

  • B由JWT用到的身份验证信息json数据加密得到

  • C由A和B加密得到,是校验部分

 

2.3:流程

 

2.4:示例

导入依赖:

<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency

编写测试用例:  

@Test
public void testCreateToken() {//生成token//1 准备数据Map map = new HashMap();map.put("id",1);map.put("mobile","13612345678");//2 使用JWT的工具类生成tokenlong now = System.currentTimeMillis();String token = Jwts.builder().signWith(SignatureAlgorithm.HS512, "czxy") //指定加密算法.setClaims(map) //写入数据.setExpiration(new Date(now + 30000)) //失效时间.compact();System.out.println(token);
}//解析token/*** SignatureException : token不合法* ExpiredJwtException:token已过期*/
@Test
public void testParseToken() {String token = "eyJhbGciOiJIUzUxMiJ9.eyJtb2JpbGUiOiIxMzgwMDEzODAwMCIsImlkIjoxLCJleHAiOjE2MTgzOTcxOTV9.2lQiovogL5tJa0px4NC-DW7zwHFqZuwhnL0HPAZunieGphqnMPduMZ5TtH_mxDrgfiskyAP63d8wzfwAj-MIVw";try {Claims claims = Jwts.parser().setSigningKey("czxy").parseClaimsJws(token).getBody();Object id = claims.get("id");Object mobile = claims.get("mobile");System.out.println(id + "--" + mobile);}catch (ExpiredJwtException e) {System.out.println("token已过期");}catch (SignatureException e) {System.out.println("token不合法");}}

通过解析Token得知,如果抛出SignatureException异常表示token不合法,如果抛出ExpiredJwtException异常表示token已过期  

2.5:JWT工具类

tanhua-commons 模块创建JWT工具类

public class JwtUtils {// TOKEN的有效期1小时(S)private static final int TOKEN_TIME_OUT = 3_600;// 加密KEYprivate static final String TOKEN_SECRET = "czxy";// 生成Tokenpublic static String getToken(Map params){long currentTime = System.currentTimeMillis();return Jwts.builder().signWith(SignatureAlgorithm.HS512, TOKEN_SECRET) //加密方式.setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000)) //过期时间戳.addClaims(params).compact();}/*** 获取Token中的claims信息*/private static Claims getClaims(String token) {return Jwts.parser().setSigningKey(TOKEN_SECRET).parseClaimsJws(token).getBody();}/*** 是否有效 true-有效,false-失效*/public static boolean verifyToken(String token) {if(StringUtils.isEmpty(token)) {return false;}try {Claims claims = Jwts.parser().setSigningKey(TOKEN_SECRET).parseClaimsJws(token).getBody();}catch (Exception e) {return false;}return true;}
}

 

影视作品