• 【探花交友】用户登录、代码优化


    文章目录

    7、用户登录

    7.1、登录验证码

    7.2、JWT

    7.3、用户登录

    8、代码优化

    8.1 抽取BasePojo

    8.2 自动填充

    7、用户登录

    7.1、登录验证码

    7.1.1、接口说明

     

    参见YAPI接口地址:http://192.168.136.160:3000/project/19/interface/api/94

    7.1.2、流程分析

     

    客户端发送请求

    服务端调用第三方组件发送验证码

    验证码发送成功,存入redis

    响应客户端,客户端跳转到输入验证码页面

    7.1.3、代码实现

    LoginController

    @RestController
    @RequestMapping("/user")
    public class LoginController {
    ​
        @Autowired
        private UserService userService;
    ​
        /**
         * 获取登录验证码
         *   请求参数:phone (Map)
         *   响应:void
         */
        @PostMapping("/login")
        public ResponseEntity login(@RequestBody Map map){
            String phone =(String) map.get("phone");
            userService.sendMsg(phone);
            return ResponseEntity.ok(null); //正常返回状态码200
        }
    }

    UserService

    @Service
    public class UserService {
    ​
        @Autowired
        private SmsTemplate template;
    ​
        @Autowired
        private RedisTemplate redisTemplate;
    ​
        /**
         * 发送短信验证码
         * @param phone
         */
        public void sendMsg(String phone) {
            //1、随机生成6位数字
            //String code = RandomStringUtils.randomNumeric(6);
            String code = "123456";
            //2、调用template对象,发送手机短信
            //template.sendSms(phone,code);
            //3、将验证码存入到redis
            redisTemplate.opsForValue().set("CHECK_CODE_"+phone,code, Duration.ofMinutes(5));
        }
     }

    7.2、JWT

    7.2.1、简介

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

    7.2.2、格式

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

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

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

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

     

    7.2.3、流程

     

    7.2.4、示例

    导入依赖:

    
        io.jsonwebtoken
        jjwt
        0.9.1
    

    编写测试用例:

        @Test
        public void testCreateToken() {
            //生成token
            //1、准备数据
            Map map = new HashMap();
            map.put("id",1);
            map.put("mobile","13800138000");
            //2、使用JWT的工具类生成token
            long now = System.currentTimeMillis();
            String token = Jwts.builder()
                    .signWith(SignatureAlgorithm.HS512, "itcast") //指定加密算法
                    .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("itcast")
                        .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已过期

    7.2.5 JWT工具类

    public class JwtUtils {
    ​
        // TOKEN的有效期1小时(S)
        private static final int TOKEN_TIME_OUT = 1 * 3600;
    ​
        // 加密KEY
        private static final String TOKEN_SECRET = "itcast";
    ​
    ​
        // 生成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信息
         */
        public 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("itcast")
                        .parseClaimsJws(token)
                        .getBody();
            }catch (Exception e) {
                return false;
            }
    ​
          return true;
        }
    }

    7.3、用户登录

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

     

    7.3.1、接口文档

     

    YAPI接口地址:YApi-高效、易用、功能强大的可视化接口管理平台

    7.3.2、LoginController

        /**
         * 检验登录
         */
        @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);
        }

    7.3.3、UserService

        /**
         * 验证登录
         * @param phone
         * @param code
         */
        public Map loginVerification(String phone, String code) {
            //1、从redis中获取下发的验证码
            String redisCode = redisTemplate.opsForValue().get("CHECK_CODE_" + phone);
            //2、对验证码进行校验(验证码是否存在,是否和输入的验证码一致)
            if(StringUtils.isEmpty(redisCode) || !redisCode.equals(code)) {
                //验证码无效
                 throw new RuntimeException();
            }
            //3、删除redis中的验证码
            redisTemplate.delete("CHECK_CODE_" + phone);
            //4、通过手机号码查询用户
            User user = userApi.findByMobile(phone);
            boolean isNew = false;
            //5、如果用户不存在,创建用户保存到数据库中
            if(user == null) {
                user = new User();
                user.setMobile(phone);
                user.setPassword(DigestUtils.md5Hex("123456"));
                Long userId = userApi.save(user);
                user.setId(userId);
                isNew = true;
            }
            //6、通过JWT生成token(存入id和手机号码)
            Map tokenMap = new HashMap();
            tokenMap.put("id",user.getId());
            tokenMap.put("mobile",phone);
            String token = JwtUtils.getToken(tokenMap);
            //7、构造返回值
            Map retMap = new HashMap();
            retMap.put("token",token);
            retMap.put("isNew",isNew);
    ​
            return retMap;
        }

    7.3.4、测试

     

    8、代码优化

    8.1 抽取BasePojo

    为了简化实体类中created和updated字段,抽取BasePojo

    @Data
    public abstract class BasePojo implements Serializable {
    ​
        @TableField(fill = FieldFill.INSERT) //自动填充
        private Date created;
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private Date updated;
    ​
    }

    8.2 自动填充

    对于created和updated字段,每次操作都需要手动设置。为了解决这个问题,mybatis-plus支持自定义处理器的形式实现保存更新的自动填充

    package com.tanhua.dubbo.server.handler;
    ​
    import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
    import org.apache.ibatis.reflection.MetaObject;
    import org.springframework.stereotype.Component;
    ​
    import java.util.Date;
    ​
    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
    ​
        @Override
        public void insertFill(MetaObject metaObject) {
            Object created = getFieldValByName("created", metaObject);
            if (null == created) {
                //字段为空,可以进行填充
                setFieldValByName("created", new Date(), metaObject);
            }
    ​
            Object updated = getFieldValByName("updated", metaObject);
            if (null == updated) {
                //字段为空,可以进行填充
                setFieldValByName("updated", new Date(), metaObject);
            }
        }
    ​
        @Override
        public void updateFill(MetaObject metaObject) {
            //更新数据时,直接更新字段
            setFieldValByName("updated", new Date(), metaObject);
        }
    }
  • 相关阅读:
    在linux上做移动开发必须知道这五个
    SpringBoot读取json文件
    [附源码]JAVA毕业设计互联网保险网站(系统+LW)
    【11. 死锁和进程通信】
    tcpdump使用技巧
    Pythonmock基本使用
    uboot指令笔记
    猿创征文|数据导入与预处理-第2章-numpy
    【漏洞复现】Apache_HTTP_2.4.50_路径穿越漏洞(CVE-2021-42013)
    详细的科技特长生路径和成长规划
  • 原文地址:https://blog.csdn.net/weixin_45481821/article/details/126849027