• 谷粒学院16万字笔记+1600张配图(十四)——注册、登录


    项目源码与所需资料
    链接:https://pan.baidu.com/s/1azwRyyFwXz5elhQL0BhkCA?pwd=8z59
    提取码:8z59

    文章目录

    demo14-注册、登录

    1.用户登录业务介绍(单点登录)

    1.1单一服务器模式

    1.早期用的都是单一服务器。比如说我现在有一个程序,里面有登录在内的各种功能,然后我把这个程序部署到一台tomcat中,这种用一台服务器运行程序的方式就叫做单一服务器模式

    2.单一服务器模式的缺点:单点性能压力,无法扩展

    3.单一服务器模式下判断用户是否登录可以使用session对象实现:用户登录成功后,我们把用户数据放到session域对象中,这样的话判断用户是否登录时就可以从session中获取数据,如果可以获取数据那就是已登录,如果不能获取数据那就是未登录。但这种方式只适合单一服务器模式下使用,如果是分布式或集群,这样做会出问题的

    4.解释一下为什么在分布式下使用session域对象会出问题:

    在这里插入图片描述

    用户在service_edu服务登录后,使用session.setAttribute("user",user)在service_edu服务的session域对象中存入用户数据,但是此时在线教育项目的其它项目(service_oss、service_vod、service_cms…)的session域对象中并没有存入该用户的数据,所以当这个用户明明已经在service_edu服务登录过了,但是访问在线教育项目的其它服务(service_oss、service_vod、service_cms…)时仍需登录,这就是问题

    5.我们理想效果肯定是:用户在在线教育项目下的任何一个服务登录后,再访问该项目下的其它服务时都不再需要登录,这个理想效果有一个专业术语:单点登录

    6.单点登录示例:我们在百度官网的任意一个服务(比如百度翻译)登录后,再进入其它服务(如百度文库、百度百科、百度贴吧…)都不再需要登录

    在这里插入图片描述

    1.2SSO(single sign on)模式

    SSO模式和刚刚说的单点登录是一个意思,没任何区别

    1.2.1单点登录三种常见方式
    • 使用session域对象的广播机制实现单点登录(现在用的不多了)
    • 使用cookie和redis实现单点登录(常用)
    • 使用token(令牌机制)实现单点登录(常用)
    1.2.2session广播机制

    1.session的广播机制说通俗点就叫session复制:当用户在service_edu服务登录后,我们先使用session.setAttribute("user",user)在service_edu服务的session域对象中存入用户数据,然后将service_edu服务的session对象复制到其他服务中

    2.这种方式有一个致命的缺点:如果我们一个项目中有几十个模块,那么就需要将session对象复制几十次,这对资源是一个极大的消耗

    1.2.3cookie+redis

    cookie的特点:是一个客户端技术,浏览器每次发送请求都会带着cookie值进行发送;redis的特点是:基于key-value存储数据

    具体实现:

    1.在项目的任意一个模块进行登录后,把数据放到两个地方:redis和cookie

    • redis:按照一定规则(规则不是固定的,我们可以根据需求制定规则)生成唯一、随机的一个值,将这个值放到key中;将用户数据放到value中
    • cookie:把redis里面生成的key值放到cookie中

    2.用户访问项目中其它模块时,发送请求会带着这个cookie值进行发送,然后我们获取到这个cookie值,拿着获取到的cookie值去redis中根据key进行查询,如果可以查询到对应的value就说明此时用户是登录状态

    1.2.4token(令牌机制)

    token是什么:按照一定规则(规则不是固定的,我们可以根据需求制定规则)生成的字符串,这个字符串中可以包含用户信息

    比如说我们制定的规则是:ip+用户名+用户年龄,假设是192.1.1.1lucy22,然后我们将这串字符进行base64编码,再做一个加密,最后就得到了token字符串

    具体实现:

    1.在项目的任意一个模块进行登录后,我们按照一定规则生成一个token字符串,要求这个字符串中包含用户信息,然后将这个字符串进行返回(两种方式返回:把字符串通过cookie返回、把字符串通过地址栏返回)

    通过地址栏返回的示例:

    在这里插入图片描述

    2.用户访问项目中其它模块时,每次访问在地址栏都带着生成的这个token字符串,我们得到地址栏中的这个token字符串,将这个字符串解码,然后就可以获取到用户信息

    1.2.5过期时间

    我们在javaweb阶段学过,session的默认过期时间是30分钟,其实上面的第二种方式(使用cookie+redis)和第三种方式(使用token)我们也可以设置过期时间:

    • 通过设置redis的过期时间来规定第二种方式的过期时间
    • 生成token时我们可以设置过期时间,后面会说到

    2.整合JWT

    2.1JWT是什么

    在"1.2.4token(令牌机制)"我们说过了,我们按照一定规则生成token字符串,JWT就是给我们制定好了规则,我们使用JWT规则可以生成token字符串,且这个字符串中包含用户信息

    2.2JWT的组成

    在这里插入图片描述

    JWT字符串由三部分组成:

    第一部分(红色):jwt头信息

    第二部分(紫色):有效载荷,含有用户信息(也可以说是主体信息)

    第三部分(蓝色):签名哈希,通俗说就是字符串的防伪标志,通过这个可以判断这个字符串是我们自己根据JWT规则生成的还是别人伪造的

    2.3引入依赖

    因为jwt我们在后面做注册、登录或其它功能时会用到jwt,所以将依赖添加到common_utils模块中(别忘了刷新maven)

    <dependencies>
        
        <dependency>
            <groupId>io.jsonwebtokengroupId>
            <artifactId>jjwtartifactId>
        dependency>
    dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    2.4创建jwt工具类

    1.jwt工具类不用自己会写,能够根据需求修改就可以了,我把jwt工具类放到了资料中

    在这里插入图片描述

    2.我们将jwt工具类复制到common_utils模块的commonutils包下

    在这里插入图片描述

    3.分析一下这个jwt工具类

    在这里插入图片描述

    • 截图中第19行的public static final long EXPIRE = 1000 * 60 * 60 * 24;:我们定义一个常量,用于设置token过期时间
    • 截图中第20行的public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";:这是一个秘钥(这个秘钥是保密的,以后公司会给我们),我们后面生成token的第三部分(签名哈希)时会用到这个秘钥
    • 截图中第22行的getJwtToken方法:这是一个生成token字符串的方法,我们这里只给这个方法两个参数,分别是用户id和用户名称。接下来还会说这个方法内部的具体代码,理解后其实这个方法三个或四个或五个或其它任意数量参数,我们就都会修改方法内部的代码了
    • 截图中第25行的.setHeaderParam("typ", "JWT")和第26行的.setHeaderParam("alg", "HS256")共同作用,目的是设置token字符串的第一部分(jwt头信息),这是规定的,我们不需要修改,也不能修改
    • 截图中第27行的.setSubject("guli-user")、第28行的.setIssuedAt(new Date())、第29行的.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))共同作用,目的是设置token字符串的过期时间,其中第27行的参数guli-user可以随便写
    • 截图中第30行的.claim("id", id)和第31行的.claim("nickname", nickname)共同作用,目的是设置token字符串的第二部分(主体信息),用来存储用户信息。如果getJwtToken方法有第三个参数age,那么我们就可以再加上一行.claim("age", age)
    • 截图中第32行的.signWith(SignatureAlgorithm.HS256, APP_SECRET)和第33行的.compact();共同作用,目的是设置token字符串的第三部分(签名哈希)
    • 截图中第43行和第59行都是checkToken方法,这是方法重载,两个方法都是用来判断token是否存在与有效。第59行的checkToken方法:为了操作方便,我们项目中会把token字符串放到header中,所以就需要先通过request.getHeader("token")得到这个字符串,然后再进行判断
    • 截图中第76行的getMemberIdByJwtToken方法:根据token字符串获取用户信息,这个方法获取的是用户id并返回,以便我们拿着用户id后去数据库查询用户所有信息

    3.开通阿里云短信服务

    1.在阿里云官网首页的搜索栏搜索"短信服务",然后点击"短信服务"

    在这里插入图片描述

    2.点击"免费开通"即可开通阿里云短信服务

    在这里插入图片描述

    3.进入到短信服务的控制台,注意"快速学习"菜单下的签名名称(阿里云短信测试)和模板Code(SMS_154950909),这两个数据我们后面会用

    在这里插入图片描述

    4.点击"快速学习"菜单下的"绑定测试手机号"来添加一个测试手机号

    在这里插入图片描述

    5.在阿里云官网首页点击"试用中心"

    在这里插入图片描述

    6.在搜索栏搜索"短信"并按回车,可以看到有可使用的短信产品,我们点击"0元试用"

    在这里插入图片描述

    7.然后傻瓜式下一步,出现下图这个页面后,就说明成功了,此时我们已经有了100条免费短信(3个月后失效)

    在这里插入图片描述

    4.整合阿里云短信服务

    4.1新建短信微服务

    4.1.1创建子子模块service_msm

    1.在service模块上右键选择New–>Module…

    在这里插入图片描述

    2.创建一个Maven项目

    在这里插入图片描述

    3.填写信息后点击"Finish"

    在这里插入图片描述

    4.1.2创建项目结构

    1.在java包下创建包com.atguigu.msmservice,然后在msmservice包下创建启动类MsmApplication,并在启动类中添加代码

    @ComponentScan({"com.atguigu"})
    @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源自动配置
    public class MsmApplication {
        public static void main(String[] args) {
            SpringApplication.run(MsmApplication.class, args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    2.在msmservice包下创建包controller,然后在controller包下创建控制器MsmController,并在控制器上添加注解

    @RestController
    @RequestMapping("/edumsm/msm")
    @CrossOrigin
    public class MsmController {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    3.在msmservice包下创建包service,然后:①在service包下创建业务层接口MsmService②在service包下创建包impl,然后在impl包下创建业务层实现类MsmServiceImpl并使其实现MsmService接口

    @Service
    public class MsmServiceImpl implements MsmService {
    }
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    4.1.3配置application.properties

    创建配置application.properties文件并编写配置

    # 服务端口
    server.port=8005
    # 服务名
    spring.application.name=service-msm
    
    spring.redis.host=192.168.111.100
    spring.redis.port=6379
    spring.redis.database= 0
    spring.redis.timeout=1800000
    
    spring.redis.lettuce.pool.max-active=20
    spring.redis.lettuce.pool.max-wait=-1
    #最大阻塞等待时间(负数表示没限制)
    spring.redis.lettuce.pool.max-idle=5
    spring.redis.lettuce.pool.min-idle=0
    #最小空闲
    
    #返回json的全局时间格式
    spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
    spring.jackson.time-zone=GMT+8
    
    #mybatis日志
    mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    注意:截图中第6行的192.168.111.100是我linux虚拟机的ip地址,你们填你们自己的

    4.2添加依赖

    在service_msm模块的pom.xml中添加依赖(记得刷新maven)

    <dependencies>
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
        dependency>
        <dependency>
            <groupId>com.aliyungroupId>
            <artifactId>aliyun-java-sdk-coreartifactId>
        dependency>
    dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    4.3创建随机生成数字的工具类

    1.阿里云只负责给手机号发送验证码,生成验证码的过程是我们自己完成的,我们这里使用工具类来生成随机数字,工具类在资料中给出了

    在这里插入图片描述

    2.在service_msm模块的msmservice包下创建包utils,然后将工具类RandomUtil.java复制到utils包下

    在这里插入图片描述

    4.4控制层

    在service_msm模块的控制器MsmController中编写代码

    @Autowired
    private MsmService msmService;
    
    //发送短信的方法
    @GetMapping("send/{phone}")
    public R sendMsm(@PathVariable String phone) {
        //生成4位随机数字
        String code = RandomUtil.getFourBitRandom();
        Map<String,Object> param = new HashMap<>();
        param.put("code", code);
    
        //调用service中发送短信的方法
        boolean isSend = msmService.send(param, phone);
        if (isSend) {
            return R.ok();
        } else {
            return R.error().message("短信发送失败");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    为什么要把code放到Map集合中传递过去?

    在后面的"4.6业务层实现类"的截图中的第39行request.putQueryParameter("TemplateParam", JSONObject.toJSONString(param));:人家阿里云规定了,当键为TemplateParam时,putQueryParameter方法的第二个参数必须传的数据格式是json格式,我们这里在控制层将code放到Map后传给业务层,那么业务层只需要使用JSONObject.toJSONString(param)就可以将Map集合转为json格式并传递

    4.5业务层接口

    在业务层接口MsmService中定义发送短信的抽象方法

    //发送短信
    boolean send(Map<String, Object> param, String phone);
    
    • 1
    • 2

    在这里插入图片描述

    4.6业务层实现类

    在业务层实现类MsmServiceImpl中实现上一步定义的抽象方法

    //发送短信
    @Override
    public boolean send(Map<String, Object> param, String phone) {
        if(StringUtils.isEmpty(phone)) return false;
    
        DefaultProfile profile = DefaultProfile.getProfile(
        "default","LTAI5tMUCkxmE6ouUc2dmbXm","0Py10jHOPVkeFp6MiIm88c9QqyykUE");
        IAcsClient client = new DefaultAcsClient(profile);
    
        //设置相关参数(固定的,不需要修改)
        CommonRequest request = new CommonRequest();
        request.setMethod(MethodType.POST); //提交方式
        request.setDomain("dysmsapi.aliyuncs.com"); //发送时要访问阿里云中的哪个地方
        request.setVersion("2017-05-25"); //版本号
        request.setAction("SendSms"); //请求里面的哪个方法
    
        //设置发送的相关参数
        request.putQueryParameter("PhoneNumbers", phone); //设置要发送的手机号
        request.putQueryParameter("SignName", "阿里云短信测试"); //在阿里云申请的签名名称
        request.putQueryParameter("TemplateCode", "SMS_154950909"); //在阿里云中申请的模板Code
        request.putQueryParameter("TemplateParam", JSONObject.toJSONString(param)); //验证码数据
    
        try {
            //最终发送
            CommonResponse response = client.getCommonResponse(request);
    
            boolean success = response.getHttpResponse().isSuccess();
            return success;
        } catch(Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    在这里插入图片描述

    • 截图中第22行的if(StringUtils.isEmpty(phone)) return false;:判断手机号是否为空,如果为空就不发送短信,如果不为空就执行接下来的代码
      • 注意:这里的工具类StringUtils导的是spring包下的,别导错包了
    • 截图中第24行的getProfile方法的第一个参数是地域节点,我们用默认的就可以、第二个参数和第三个参数分别是阿里云的id和秘钥,填写自己的
    • 截图中第29-33行是固定的,不需要修改,也不能修改
    • 截图中第36、37、38、39行的PhoneNumbers、SignName、TemplateCode、TemplateParam是固定的
    • 还记得在"3.开通阿里云短信服务"的第3步说的签名名称(阿里云短信测试)和模板Code(SMS_154950909)吗?截图中第37行和38行用到了

    4.7测试

    1.在nginx中配置8005端口

    location ~ /edumsm/ {
    	proxy_pass http://localhost:8005;
    }
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    2.将service_msm服务注册到注册中心,这样做的原因在"demo12-课程管理"的"4.4问题",具体步骤在"demo12-课程管理"的"4.3服务注册(service_vod)",这里不再演示,自行配置吧

    3.启动MsmApplication服务,使用swagger进行测试。注意:输入的手机号必须是在"3.开通阿里云短信服务"的第4步添加的手机号

    在这里插入图片描述

    在这里插入图片描述

    4.8整合redis

    1.实际场景中验证码是在一定时间内有效(比如5分钟内有效),但是阿里云只负责将验证码发给用户,并不会管理验证码的失效时间

    2.解决方法:我们在后端将验证码存到redis中,并设置有效时间

    3.但是这里使用redis的目的和在"demo13-搭建前台环境、首页数据显示"的"10.Redis"中的目的不一样,我们在那里使用redis是为了缓存,而我们这里使用redis是为了设置验证码的有效时间,所以我们这里换一种方式来使用redis

    4.我们的业务逻辑是:发送验证码时先从redis中取,如果能从redis取到验证码,那就说明该手机号此时有一个有效的验证码,无需再次给该手机号发送验证码;如果不能从redis取到验证码,那就说明该手机号此时没有可用验证码,需要使用阿里云给该手机号发送验证码

    5.SpringBoot整合redis时人家给我们封装了一个RedisTemplate对象,现在我们在控制器MsmController中注入这个对象

    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    
    • 1
    • 2

    在这里插入图片描述

    6.在控制器MsmController的sendMsm方法中添加两段代码

    //1.从redis中获取验证码,如果能取到就不需要使用阿里云发送验证码,我们直接返回
    String code = redisTemplate.opsForValue().get(phone);
    if (!StringUtils.isEmpty(code)) {
        return R.ok();
    }
    //2.如果不能从redis中获取到,就使用阿里云发送验证码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    //发送成功,把发送成功的验证码放到redis里面并且设置有效时长
    redisTemplate.opsForValue().set(phone, code,5, TimeUnit.MINUTES);
    
    • 1
    • 2

    在这里插入图片描述

    • 绿方框圈起来的String需要删掉
    • 截图中第44行的set方法的第一个参数phone和第二个参数code是key-value的关系
    • 截图中第44行的set方法的第三个参数5和第四个参数TimeUnit.MINUTES共同作用,目的是:存到redis中的验证码有效时长是5分钟

    4.9再次测试

    1.Xshell连接上虚拟机后,先使用命令cd /usr/local/bin进入该目录,然后在该目录使用如下命令启动redis,并且启动时使用的配置文件是etc目录下的redis.conf

    redis-server /etc/redis.conf
    
    • 1

    在这里插入图片描述

    2.在bin目录使用redis-cli命令,目的是:在本地客户端(也就是linux虚拟机)连接linux虚拟机中的redis

    在这里插入图片描述

    3.重启后端项目,使用swagger进行测试

    在这里插入图片描述

    在这里插入图片描述

    4.使用get xxx命令看一下能否获取到验证码数据(xxx是我们测试时填写的手机号)

    在这里插入图片描述

    可以看到能获取到验证码数据,说明我们成功将该手机号的验证码存到了redis中

    5.用户登录注册(后端)

    5.1新建用户微服务

    5.1.1创建子子模块service_ucenter

    1.在service模块上右键选择New–>Module…

    在这里插入图片描述

    2.创建一个Maven项目

    在这里插入图片描述

    3.填写信息后点击"Finish"

    在这里插入图片描述

    5.1.2创建数据表ucenter_member

    1.创建这张表的脚本在资料的guli_ucenter.sql文件中

    在这里插入图片描述

    在这里插入图片描述

    2.将创建ucenter_member表的脚本复制到数据库中执行

    CREATE TABLE `ucenter_member` (
      `id` char(19) NOT NULL COMMENT '会员id',
      `openid` varchar(128) DEFAULT NULL COMMENT '微信openid',
      `mobile` varchar(11) DEFAULT '' COMMENT '手机号',
      `password` varchar(255) DEFAULT NULL COMMENT '密码',
      `nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
      `sex` tinyint(2) unsigned DEFAULT NULL COMMENT '性别 1 女,2 男',
      `age` tinyint(3) unsigned DEFAULT NULL COMMENT '年龄',
      `avatar` varchar(255) DEFAULT NULL COMMENT '用户头像',
      `sign` varchar(100) DEFAULT NULL COMMENT '用户签名',
      `is_disabled` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否禁用 1(true)已禁用,  0(false)未禁用',
      `is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
      `gmt_create` datetime NOT NULL COMMENT '创建时间',
      `gmt_modified` datetime NOT NULL COMMENT '更新时间',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员表';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    5.1.3生成代码

    1.在service_ucenter模块的test包的java包下创建包demo.codedemo

    在这里插入图片描述

    2.将service_cms模块的代码生成器CodeGenerator复制到上一步创建的codedemo包下

    在这里插入图片描述

    3.修改service_ucenter模块的代码生成器中的部分代码

    在这里插入图片描述

    4.在run方法上右键选择"Run ‘run()’"就可以生成代码了

    在这里插入图片描述

    5.给生成的控制器UcenterMemberController添加注解@CrossOrigin以实现跨域,并且将请求路径/educenter/ucenter-member中的ucenter-member改为member

    在这里插入图片描述

    5.1.4配置application.properties

    创建配置文件application.properties并编写配置

    # 服务端口
    server.port=8006
    # 服务名
    spring.application.name=service-ucenter
    
    # mysql数据库连接
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/guli?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
    spring.datasource.username=root
    spring.datasource.password=root
    
    spring.redis.host=192.168.111.100
    spring.redis.port=6379
    spring.redis.database= 0
    spring.redis.timeout=1800000
    spring.redis.lettuce.pool.max-active=20
    spring.redis.lettuce.pool.max-wait=-1
    #最大阻塞等待时间(负数表示没限制)
    spring.redis.lettuce.pool.max-idle=5
    spring.redis.lettuce.pool.min-idle=0
    #最小空闲
    
    #返回json的全局时间格式
    spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
    spring.jackson.time-zone=GMT+8
    
    #配置mapper xml文件的路径
    mybatis-plus.mapper-locations=classpath:com/atguigu/educenter/mapper/xml/*.xml
    
    #mybatis日志
    mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    在这里插入图片描述

    注意:数据库账号密码和虚拟机ip填写自己的

    5.1.5创建启动类

    在educenter包下创建启动类UcenterApplication

    @SpringBootApplication
    @ComponentScan({"com.atguigu"}) //指定扫描位置
    @MapperScan("com.atguigu.educenter.mapper")
    public class UcenterApplication {
        public static void main(String[] args) {
            SpringApplication.run(UcenterApplication.class, args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    5.1.6配置nginx、nacos

    1.在nginx中配置8006端口并重启nginx

    location ~ /educenter/ {
    	proxy_pass http://localhost:8006;
    }
    
    • 1
    • 2
    • 3

    2.将service_ucenter服务注册到注册中心,这样做的原因在"demo12-课程管理"的"4.4问题",具体步骤在"demo12-课程管理"的"4.3服务注册(service_vod)",这里不再演示,自行配置吧

    5.2控制层(登录功能)

    在控制器UcenterMemberController中编写代码

    @Autowired
    private UcenterMemberService memberService;
    
    //登录
    @GetMapping("login")
    public R loginUser(@RequestBody UcenterMember member) {
        //业务层的登录方法login返回一个token值
        String token = memberService.login(member);
        return R.ok().data("token", token);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    5.3业务层接口(登录功能)

    在业务层接口UcenterMemberService中定义登录的抽象方法

    //登录
    String login(UcenterMember member);
    
    • 1
    • 2

    在这里插入图片描述

    5.4业务层实现类(登录功能)

    1.我们先去看ucenter_member表,可以看到每条用户数据中都有这三个字段:mobile、password、is_disabled,所以我们判断用户能否登录时把这三个字段都判断一下

    在这里插入图片描述

    2.后期做用户注册功能时我们是这样存用户密码的:先将密码进行MD5加密,将加密得到的数据作为用户密码存到数据库。将密码进行MD5加密的工具类在资料中提供了,我们将这个工具类复制到common_utils模块的commonutils包下

    在这里插入图片描述

    在这里插入图片描述

    3.在业务层实现类UcenterMemberServiceImpl中实现刚刚定义的抽象方法

    //登录
    @Override
    public String login(UcenterMember member) {
        //获取手机号和密码
        String mobile = member.getMobile();
        String password = member.getPassword();
    
        //手机号和密码非空判断
        if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {
            throw new GuliException(20001, "手机号、密码为空");
        }
    
        //判断手机号是否正确
        QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
        wrapper.eq("mobile", mobile);
        UcenterMember mobileMember = baseMapper.selectOne(wrapper);
        //判断查询对象是否正确
        if (mobileMember == null) { //数据表中没有这个手机号
            throw new GuliException(20001, "没有这个手机号数据");
        }
    
        //判断密码
        //把用户输入的密码进行MD5加密,然后和数据库中的密码进行比较
        if (!MD5.encrypt(password).equals(mobileMember.getPassword())) {
            throw new GuliException(20001, "密码错误");
        }
    
        //判断用户是否禁用
        if (mobileMember.getIsDisabled()) {
            throw new GuliException(20001, "用户已禁用");
        }
    
        //登录成功,生成token字符串
        String jwtToken = JwtUtils.getJwtToken(mobileMember.getId(), mobileMember.getNickname());
    
        return jwtToken;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    在这里插入图片描述

    截图中第58行生成token字符串时使用的从数据库查到的mobileMember对象而不是前端传过来的member对象,因为member对象是从前端传过来的,里面只有手机号和密码,并没有用户id和用户名称(nickname)

    5.5测试(登录功能)

    1.启动服务,使用swagger进行测试(注意:数据库中不需要有数据,使用swagger时参数随便写就行,因为这次测试不是最重要的,重要的是后面会引出一个问题)

    可以看到,有异常,说明我们后端代码有问题

    在这里插入图片描述

    2.看下控制台报错信息

    在这里插入图片描述

    org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public com.atguigu.commonutils.R com.atguigu.educenter.controller.UcenterMemberController.loginUser(com.atguigu.educenter.entity.UcenterMember)
    
    • 1

    3.以后只要我们看到了Required request body is missing就先看下提交方式是否正确,可以看到我们UcenterMemberController的loginUser方法的提交方式是get方式

    在这里插入图片描述

    4.这里使用get提交是不对的:因为loginUser方法的参数用了注解@RequestBody,所以不能使用get提交(因为get提交没有请求体),需要使用post提交。所以我们将loginUser方法的提交方式改为post方式

    在这里插入图片描述

    5.重启项目,重新进行测试,可以看到不再像刚刚测试那样抛出全局异常(当然,我们数据表ucenter_member中本来就没有数据,所以需要抛出"没有这个手机号数据"的异常)

    在这里插入图片描述

    5.6创建VO类(注册功能)

    1.用户注册时前端会传来用户的昵称、手机号、密码、验证码,但是实体类UcenterMember中并没有定义验证码变量,所以我们需创建一个VO类来封装用户的昵称、手机号、密码、验证码

    2.在entity包下创建包vo,然后在vo包下创建vo类RegisterVo

    @Data
    public class RegisterVo {
        @ApiModelProperty(value = "昵称")
        private String nickname;
        @ApiModelProperty(value = "手机号")
        private String mobile;
        @ApiModelProperty(value = "密码")
        private String password;
        @ApiModelProperty(value = "验证码")
        private String code;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    5.7控制层(注册功能)

    在控制器UcenterMemberController中编写代码

    //注册
    @PostMapping("register")
    public R registerUser(@RequestBody RegisterVo registerVo) {
        memberService.register(registerVo);
        return R.ok();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    5.8业务层接口(注册功能)

    在业务层接口UcenterMemberService中定义注册的抽象方法

    //注册
    void register(RegisterVo registerVo);
    
    • 1
    • 2

    在这里插入图片描述

    5.9业务层实现类(注册功能)

    1.因为我们业务逻辑中有一个操作是:从redis中获取验证码,并和用户输入的验证码进行比较。所以需要在业务层实现类UcenterMemberServiceImpl中注入RedisTemplate对象

    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    
    • 1
    • 2

    在这里插入图片描述

    2.在业务层实现类UcenterMemberServiceImpl中实现上一步定义的抽象方法

    //注册
    @Override
    public void register(RegisterVo registerVo) {
        //获取注册的数据
        String nickname = registerVo.getNickname(); //昵称
        String mobile = registerVo.getMobile(); //手机号
        String password = registerVo.getPassword(); //密码
        String code = registerVo.getCode(); //验证码
    
        //非空判断
        if (StringUtils.isEmpty(nickname) ||
            StringUtils.isEmpty(mobile) ||
            StringUtils.isEmpty(password) ||
            StringUtils.isEmpty(code)) {
            throw new GuliException(20001, "注册失败");
        }
    
        //判断验证码是否正确
        //先从redis中取得验证码
        String redisCode = redisTemplate.opsForValue().get(mobile);
        if (!code.equals(redisCode)) {
            throw new GuliException(20001, "注册失败");
        }
    
        //判断手机号是否重复
        //如果表中存在相同的手机号,那就不允许进行添加
        QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
        wrapper.eq("mobile", mobile);
        Integer count = baseMapper.selectCount(wrapper);
        if (count > 0) { //数据表中已经有了这个手机号
            throw new GuliException(20001, "注册失败");
        }
    
        //将数据添加到数据库中
        UcenterMember member = new UcenterMember();
        member.setNickname(nickname);
        member.setMobile(mobile);
        member.setPassword(MD5.encrypt(password));
        member.setIsDisabled(false); //用户未禁用
        member.setAvatar("https://edu-mxy.oss-cn-hangzhou.aliyuncs.com/2022/08" +
                         "/28/e97af8298b4c481695cc7723c01c614a1243.jpg"); //用户默认头像
        baseMapper.insert(member);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    在这里插入图片描述

    5.10自动填充

    给实体类UcenterMember的gmtCreate字段和gmtModified字段都添加@TableField注解以实现自动填充

    在这里插入图片描述

    5.11测试

    1.重启后端项目,启动虚拟机中的redis服务

    2.先使用8005端口的swagger得到验证码并将验证码存到redis中

    在这里插入图片描述

    3.再使用8006端口的swagger测试能否注册成功

    在这里插入图片描述

    4.去数据中可以看到我们成功添加了这条数据

    在这里插入图片描述

    5.再测试一下登录,可以看到登录成功并且给我们返回了token字符串

    在这里插入图片描述

    6.根据token获取用户信息(后端)

    6.1分析

    1.我们的需求是:用户成功登录后在页面的右上角可以显示用户昵称、头像,所以我们就需要根据token获取到用户信息显示

    2.在工具类JwtUtils中已经给出了"根据token获取用户信息"的方法,这个方法的返回值是用户id,我们后端得到这个方法返回的用户id后去数据库中查询就可以得到用户的所有信息了

    在这里插入图片描述

    6.2控制层

    在控制器UcenterMemberController中编写代码

    //根据token获取用户信息
    @GetMapping("getMemberInfo")
    public R getMemberInfo(HttpServletRequest request) {
        //调用jwt工具类的方法,该方法内部:根据request对象获取请求头中的token,然后就可以返回用户id
        String memberId = JwtUtils.getMemberIdByJwtToken(request);
        //查询数据库,根据用户id得到用户信息
        UcenterMember member = memberService.getById(memberId);
        return R.ok().data("userInfo", member);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    7.用户登录注册(前端)

    7.1在NUXT环境中安装插件

    7.1.1安装element-ui和vue-qriously
    • 我们在"demo13-搭建前台环境、首页数据显示"的"2.2NUXT目录结构"说过,NUXT环境并没有集成element-ui,如果我们想使用element-ui就需要将element-ui引入过来
    • vue-qriously暂时用不到,后面微信支付会用到(这个插件用来下载二维码),我们也先将这个插件装上

    在终端中分别使用命令npm install element-uinpm install vue-qriously安装这两个插件

    在这里插入图片描述

    在这里插入图片描述

    7.1.2在NUXT环境中使用这两个插件

    修改plugins目录下的配置文件nuxt-swiper-plugin.js,使得我们可以在NUXT环境中使用这两个插件

    配置文件nuxt-swiper-plugin.js中的完整代码如下:

    import Vue from 'vue'
    import VueAwesomeSwiper from 'vue-awesome-swiper/dist/ssr'
    import VueQriously from 'vue-qriously'
    import ElementUI from 'element-ui' //element-ui的全部组件
    import 'element-ui/lib/theme-chalk/index.css'//element-ui的css
    Vue.use(ElementUI) //使用elementUI
    Vue.use(VueQriously)
    Vue.use(VueAwesomeSwiper)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    7.2创建布局页面

    1.在layouts目录下创建布局页面sign.vue,用户登录、注册时使用

    可能有朋友会问了,layouts目录下不是已经有了布局页面default.vue了,为什么还要创建一个布局页面?我们的需求是:其他页面使用default.vue的页面布局,登录、注册页面使用sign.vue的页面布局。当然,如果你想让所有页面都使用default.vue的页面布局,那就不用再创建布局页面sign.vue了

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    7.3修改登录和注册的超链接地址

    修改布局页面default.vue中的登录和注册的超链接地址,使得我们点击页面头部的登录、注册按钮时可以发生跳转

    在这里插入图片描述

    在这里插入图片描述

    7.4整合注册页面

    在pages目录下创建一个register.vue页面,这个页面中的代码老师已经给我们了,直接复制过来即可

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99

    代码中第90-96行的判断手机号是否合法的方法

    7.5整合登录页面

    在pages目录下创建一个login.vue页面,这个页面中的代码老师已经给我们了,直接复制过来即可

    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    7.6在api中定义方法(注册功能)

    在api目录下创建register.js文件,定义方法调用后端接口

    import request from '@/utils/request'
    export default {
      //给手机号发送验证码
      sendCode(phone) {
        return request({
          url: `/edumsm/msm/send/${phone}`,
          method: 'get'
        })
      },
      //注册的方法
      registerMember(formItem) {
        return request({
          url: `/educenter/member/register`,
          method: 'post',
          data: formItem
        })
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    7.7倒计时效果(注册功能)

    1.实际应用场景中,发送验证码后必须等60秒后才可以再次发送验证码,这个需求的实现需要用到js中的setInterval方法,这个方法的第一个参数表示要执行的方法,第二个参数表示间隔多久执行一次第一个参数中的方法

    2.在methods: {...}中定义方法,完成需求

    //倒计时
    timeDown() {
      let result = setInterval(() => {
        --this.second;
        this.codeTest = this.second
        if (this.second < 1) {
          clearInterval(result);
          this.sending = true;
          this.second = 60;
          this.codeTest = "获取验证码"
        }
      }, 1000);
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    7.8调用api中的方法(注册功能)

    1.在register.vue页面引入上一步创建的js文件

    import registerApi from '@/api/register'
    
    • 1

    在这里插入图片描述

    2.调用api中的方法

    //注册提交的方法
    submitRegister() {
      registerApi.registerMember(this.params)
        .then(Response => {
          //提示注册成功
          this.$message({
            type: 'success',
            message: "注册成功"
          })
          //跳转到登录页面
          this.$router.push({path:'/login'})
        })
    },
    //给手机号发送验证码
    getCodeFun() {
      registerApi.sendCode(this.params.mobile)
        .then(Response => {
          this.sending = false //不能再点击,需等倒计时结束才可以再点击
          //调用倒计时方法
          this.timeDown()
        })
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    7.9判断输入框不为空、手机号合法(注册功能)

    1.当我们点击昵称输入框,但是没有输入任何东西就把光标移到别的位置,此时就会提示"请输入昵称",这个需求的实现在js中需要我们自己编写好多代码来实现,但是这个框架直接给我们封装好了,我们拿来用就行

    在这里插入图片描述

    2.并且这里还会判断手机号是否合法

    在这里插入图片描述

    7.10测试(注册功能)

    1.测试之前先将我们在"5.11测试"进行测试时插入进数据库的那条数据删掉,因为我们在业务层实现类UcenterMemberServiceImpl的register方法中有一个业务逻辑是:如果数据库中已经存在这个手机号的数据,那就不允许再添加这个手机号

    在这里插入图片描述

    2.自行测试,我的没问题(如果报跨域问题的,看下nginx是否配置,配置后是否重启,看看路径是否正确,请求方式是否正确)

    在这里插入图片描述

    在这里插入图片描述

    7.11在api中定义方法(登录功能)

    在api目录下创建login.js文件,定义方法调用后端接口

    import request from '@/utils/request'
    export default {
      //登录的方法
      submitLoginUser(userInfo) {
        return request({
          url: `/educenter/member/login`,
          method: 'post',
          data: userInfo
        })
      },
      //根据token获取用户信息
      getLoginUserInfo() {
        return request({
          url: `/educenter/member/getMemberInfo`,
          method: 'get'
        })
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    有没有同学会问:控制器UcenterMemberController的getMemberInfo方法的参数是一个HttpServletRequest对象,但是我们在api中定义的getLoginUserInfo方法调用后端接口时并没有传HttpServletRequest对象呀?这个我们后面会传的

    7.12下载插件、引入js文件(登录功能)

    1.在终端使用如下命令下载js-cookie插件(有了这个插件我们后面才可以使用cookie)

    npm install js-cookie
    
    • 1

    在这里插入图片描述

    2.在login.vue页面js文件

    import cookie from 'js-cookie'
    import loginApi from '@/api/login'
    
    • 1
    • 2

    在这里插入图片描述

    7.13分析(登录功能)

    1.登录功能实现过程中需要分四步走:

    • 调用后端登录接口,将返回的token字符串放到cookie中
    • 创建前端拦截器
      • 判断cookie中是否有token字符串,如果有,就把token字符串放到请求头(header)中
    • 根据token值调用后端接口获取用户信息,并把获取到的用户信息放到cookie中
    • 从cookie中获取用户信息并在页面进行显示

    2.为什么要把token字符串放到请求头(header)中?

    看我们的工具类JwtUtils的getMemberIdByJwtToken方法内部代码:request.getHeader("token")表示从请求头中获取token,所以我们要把token字符串放到请求头中

    在这里插入图片描述

    老师说了,把token放到cookie中后,不再把token放到请求头中也可以,这样的话修改一下工具类JwtUtils中的方法,方法内部改为从cookie中获取token,这样是可行的

    7.14调用后端登录接口(登录功能)

    //登录的方法
    submitLogin() {
      loginApi.submitLoginUser(this.user)
        .then(response => {
          //获取token字符串,将其放到cookie中
          cookie.set('guli_token',response.data.data.token,{domain: 'localhost'})
        })
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    截图中第68行的set方法:第一个参数是值的名称,我们可以把cookie理解为key-value的存储方式;第二个参数是值;第三个参数是作用范围,我们这里使用{domain: 'localhost'}表示只要访问的是localhost,就可以传递这个cookie

    7.15创建前端拦截器(登录功能)

    1.拦截器拦截的是当前的所有请求,而不是某一个请求,那怎么才能拦截所有请求呢:我们知道,api目录下的每个js文件的第一行都是使用import request from '@/utils/request'引入utils目录下的request.js文件,所以我们可以把拦截器写到utils目录下的request.js文件中(不会写没事,勉强能看懂就行)

    // http request 拦截器
    service.interceptors.request.use(
        config => {
            //debugger
            if (cookie.get('guli_token')) {
                //把获取到的cookie值放到header中
                config.headers['token'] = cookie.get('guli_token');
            }
            return config
        },
        err => {
            return Promise.reject(err);
        })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    截图中第10行的service.interceptors.request.use表示每次请求中都使用这个拦截器

    2.拦截器中使用了cookie,所以需要在request.js文件中引入js-cookie

    import cookie from 'js-cookie'
    
    • 1

    在这里插入图片描述

    3.还有一个组件,暂时用不上,不过怕后面忘了引入,所以我们这里先引入进来

    import { MessageBox, Message } from 'element-ui'
    
    • 1

    在这里插入图片描述

    4.还要一个拦截器,暂时用不上,等后面做了支付会用到,这里也先把代码放进来吧

    // http response 拦截器
    service.interceptors.response.use(
      response => {
        //debugger
        if (response.data.code == 28004) {
            console.log("response.data.resultCode是28004")
            // 返回 错误代码-1 清除ticket信息并跳转到登录页面
            //debugger
            window.location.href="/login"
            return
        }else{
          if (response.data.code !== 20000) {
            //25000:订单支付中,不做任何提示
            if(response.data.code != 25000) {
              Message({
                message: response.data.message || 'error',
                type: 'error',
                duration: 5 * 1000
              })
            }
          } else {
            return response;
          }
        }
      },
      error => {
        return Promise.reject(error.response)   // 返回接口返回的错误信息
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    在这里插入图片描述

    7.16调用后端接口获取用户信息(登录功能)

    我们在"7.14调用后端登录接口(登录功能)"定义了submitLogin方法,现在给这个方法添加代码

    //调用接口获取用户信息,将其放到cookie中
    loginApi.getLoginUserInfo()
      .then(response => {
        this.loginInfo = response.data.data.userInfo
    
        //将json对象转为json字符串,这样才能存到cookie中
        var jsonStr = JSON.stringify(this.loginInfo)
        cookie.set('guli_ucenter',jsonStr,{domain: 'localhost'})
      })
    
    //跳转到首页面(这两种方式都行)
    // this.$router.push({path:'/'})
    window.location.href = "/"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    7.17将用户信息在页面显示

    1.在layouts目录下的default.vue页面引入js-cookie

    import cookie from 'js-cookie'
    
    • 1

    在这里插入图片描述

    2.在layouts目录下的default.vue页面编写如下代码

    data() {
      return {
        token: '',
        loginInfo: { //封装用户信息
          id: '',
          age: '',
          avatar: '',
          mobile: '',
          nickname: '',
          sex: ''
        }
      }
    },
    created() {
      this.showInfo()
    },
    methods: {
      //在页面显示用户信息
      showInfo() {
        //从cookie中获取用户信息
        var userStr = cookie.get('guli_ucenter')
        //把json字符串转换为json对象
        if(userStr) { //先判断不为空
          this.loginInfo = JSON.parse(userStr)
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在这里插入图片描述

    截图中第164行是将json字符串转为json对象。为什么这里要把json字符串转为json对象才能继续接下来的操作,而我们做这个项目的前半部分时一次也没遇见过需要转为json对象才能继续接下来的操作?

    因为我们这里是先将json对象转为json字符串并放到了cookie中(在"7.16调用后端接口获取用户信息(登录功能)"的截图的第76、77行),然后又从cookie中取出json字符串。所以需要先将json字符串转为json对象才能继续接下来的操作

    3.将default.vue页面中下图用方框圈起来的部分删掉

    在这里插入图片描述

    4.将下面的代码复制到上一步删除的位置

    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    在这里插入图片描述

    截图中第

    5.自行测试,我的没问题,可以正常显示

    在这里插入图片描述

    7.18退出登录

    1.在default.vue页面定义方法实现退出功能

    //退出登录
    logout() {
      //清空cookie值
      cookie.set('guli_token','',{domain: 'localhost'})
      cookie.set('guli_ucenter','',{domain: 'localhost'})
    
      //跳转到首页
      window.location.href = "/"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    2.自行测试,我的没问题

  • 相关阅读:
    npm 安装与配置
    【技术美术实践部分】Unity Shader纹理1.0-使用单张纹理
    Unity入门02——Unity工作原理
    基于Delft3D模型水体流动、污染物对流扩散、质点运移、溢油漂移及地表水环境报告编制丨掌握模型建立、参数校准、模拟运行和结果分析等实际操作
    天津权威大数据培训机构 数据分析师的就业薪资多少?
    java版工程管理系统Spring Cloud+Spring Boot+Mybatis实现工程管理系统源码
    vscode debug python launch.json添加args不起作用
    4.1 网络层提供的两种服务
    【Vue3】全局切换字体大小
    Hive学习笔记——数据类型及DDL数据定义
  • 原文地址:https://blog.csdn.net/maxiangyu_/article/details/127032657