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

目录

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-db
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/tanhua?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false
    username: root
    password: 1234
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.136.160:8848
dubbo:
  protocol:
    name: dubbo
    port: 20881
  registry:
    address: spring-cloud://192.168.136.160:8848
  scan:
    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 {

    @Autowired
    private UserMapper userMapper;

    //根据手机号码查询用户
    public User findByMobile(String mobile) {
        QueryWrapper<User> qw = new QueryWrapper<>();
        qw.eq("mobile",mobile);
        return userMapper.selectOne(qw);
    }

    @Override
    public 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 {
    @Resource
    private SmsTemplate smsTemplate;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @DubboReference
    private 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的工具类生成token
    long 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;

    // 加密KEY
    private static final String TOKEN_SECRET = "czxy";


    // 生成Token
    public 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;
    }
}

猜你喜欢

转载自blog.csdn.net/m0_64550837/article/details/129898648
今日推荐