尚硅谷在线教育十一:登录注册业务

1. 用户登录业务介绍

1.1 单一服务器模式登录

  1. 使用session对象实现
    登录成功后,把用户数据放到session里面,判断是否登录,从session里面获取数据,可以获取到登录

1.2 单点登录

web系统由单系统发展成多系统组成的应用群,复杂性应该由系统内部承担,而不是用户。无论web系统内部多么复杂,对用户而言,都是一个统一的整体,也就是说,用户访问web系统的整个应用群与访问单个系统一样,登录/注销只要一次就够了

1.3 单点登录的常用三种方式

1.3.1. session广播机制实现

利用了session复制(如果模块过多,session复制会浪费大量的资料,占用大量的空间)session;默认过期时间30分钟

1.3.2 使用cookie+redis实现

  1. 在项目中任何一个模板进行登录,登录之后,把数据放在两个地方
  • redis:在key生成一个唯一值(ip、用户id等),在value使用用户的数据
  • cookie:把redis里面生成的cookie值放到redis里面
  1. 访问项目中的其他模块,发送请求带着cookie进行发送,获取cookie值,拿着cookie做事情:把cookie获取的值,到redis进行查询根据key进行查询,如果查询到数据就是登录

1.3.3 使用token实现

  1. 在项目某个模块进行登录,登录之后,按照规则生成字符串,把登录之后的用户信息包含到字符串里面,把字符串返回(可以把字符串通过cookie返回,也可以把字符串通过地址栏返回)
  2. 在去访问项目其他模块,每次访问在地址栏带着生成的字符串,在访问模块里面‘获取地址栏的字符串,根据字符串获取用户信息,如果可以获取到就是登录

2.使用JWT进行跨域身份验证

2.1传统用户身份验证

在这里插入图片描述

Internet服务无法与用户身份验证分开。一般过程如下:

  1. 用户向服务器发送用户名和密码。
  2. 验证服务器后,相关数据(如用户角色,登录时间等)将保存在当前会话中。
  3. 服务器向用户返回session_id,session信息都会写入到用户的Cookie。
  4. 用户的每个后续请求都将通过在Cookie中取出session_id传给服务器。
  5. 服务器收到session_id并对比之前保存的数据,确认用户的身份。
    这种模式最大的问题是,没有分布式架构,无法支持横向扩展。

2.2解决方案

  1. session广播
  2. 将透明令牌存入cookie,将用户身份信息存入redis
  3. 另外一种灵活的解决方案:
    使用自包含令牌,通过客户端保存数据,而服务器不保存会话数据。 JWT是这种解决方案的代表。

2.3JWT

2.3.1概述

JWT就是给你们规定好的规则,使用JWT规则可以生成字符串,包含用户信息

2.3.2 JWT的组成

在这里插入图片描述
该对象为一个很长的字符串,字符之间通过"."分隔符分为三个子串。
每一个子串表示了一个功能块,总共有以下三个部分:JWT头信息、有效载荷(主体部分、用户信息)和签名(防伪标志)

2.3.3 项目添加JWT工具类

2.3.3.1在common_utils模块中添加jwt工具依赖
  <!-- JWT-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>
2.3.3.2创建JWT工具类

在common_utils的模块下创建一个JwtUtils类

public class JwtUtils {
    public static final long EXPIRE = 1000 * 60 * 60 * 24;//Token过期时间
    public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO"; //密钥

    //生成Token字符串
    public static String getJwtToken(String id, String nickname){

        String JwtToken = Jwts.builder()
                //设置JWT头部分
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                .setSubject("guli-user")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))//设置过期时间

                //设置token的主体部分
                .claim("id", id)
                .claim("nickname", nickname)
                //密钥
                .signWith(SignatureAlgorithm.HS256, APP_SECRET)
                .compact();

        return JwtToken;
    }

    /**
     * 判断token是否存在与有效
     * @param jwtToken
     * @return
     */
    public static boolean checkToken(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) return false;
        try {
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 判断token是否存在与有效
     * @param request
     * @return
     */
    public static boolean checkToken(HttpServletRequest request) {
        try {
            String jwtToken = request.getHeader("token");
            if(StringUtils.isEmpty(jwtToken)) return false;
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 根据token获取会员id
     * @param request
     * @return
     */
    public static String getMemberIdByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        if(StringUtils.isEmpty(jwtToken)) return "";
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("id");
    }
}

3. 整合阿里云的短信

3.1新建短信微服务

  1. 在service模块下创建子模块service-msm
  2. application.properties配置

3.2 在阿里云中开通短信服务

  1. 开通阿里云的短信服务
  2. 进入控制台,点击国内消息,需要申请两个内容
    在这里插入图片描述
  3. 模板管理的申请
    点击模板管理,选择添加模板
    在这里插入图片描述
    进行选择
    在这里插入图片描述
  4. 添加前面管理
    选择签名管理,点击添加签名
    在这里插入图片描述
    由于现在我有些东西没有
    在这里插入图片描述

3.3 编写代码实现短信发送

3.3.1引入相关的依赖

  <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
            </dependency>
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-core</artifactId>
            </dependency>

3.3.2 application.perperties

# 服务端口
server.port=8005
# 服务名
spring.application.name=service-msm

# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=199866

spring.redis.host=47.93.21.100
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000

spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

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

3.3.3创建一个生成随机数的实体类

public class RandomUtil {
    private static final Random random = new Random();

    private static final DecimalFormat fourdf = new DecimalFormat("0000");

    private static final DecimalFormat sixdf = new DecimalFormat("000000");

    public static String getFourBitRandom() {
        return fourdf.format(random.nextInt(10000));
    }

    public static String getSixBitRandom() {
        return sixdf.format(random.nextInt(1000000));
    }

    /**
     * 给定数组,抽取n个数据
     * @param list
     * @param n
     * @return
     */
    public static ArrayList getRandom(List list, int n) {

        Random random = new Random();

        HashMap<Object, Object> hashMap = new HashMap<Object, Object>();

        // 生成随机数字并存入HashMap
        for (int i = 0; i < list.size(); i++) {

            int number = random.nextInt(100) + 1;

            hashMap.put(number, i);
        }

        // 从HashMap导入数组
        Object[] robjs = hashMap.values().toArray();

        ArrayList r = new ArrayList();

        // 遍历数组并打印数据
        for (int i = 0; i < n; i++) {
            r.add(list.get((int) robjs[i]));
            System.out.print(list.get((int) robjs[i]) + "\t");
        }
        System.out.print("\n");
        return r;
    }
}

创建一个MDK5加密的工具类

public final class MD5 {

    public static String encrypt(String strSrc) {
        try {
            char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
                    '9', 'a', 'b', 'c', 'd', 'e', 'f' };
            byte[] bytes = strSrc.getBytes();
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(bytes);
            bytes = md.digest();
            int j = bytes.length;
            char[] chars = new char[j * 2];
            int k = 0;
            for (int i = 0; i < bytes.length; i++) {
                byte b = bytes[i];
                chars[k++] = hexChars[b >>> 4 & 0xf];
                chars[k++] = hexChars[b & 0xf];
            }
            return new String(chars);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new RuntimeException("MD5加密出错!!+" + e);
        }
    }

    public static void main(String[] args) {
        System.out.println(MD5.encrypt("111111"));
    }

}

3.3.4 Controller接口

@RestController
@RequestMapping("/edumsm/msm")
@CrossOrigin
@Api(tags = "短信服务")
public class MsmController {

    @Autowired
    private MsmService msmService;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    //发送短信的方法
    @GetMapping("/send/{phone}")
    @ApiOperation("发送短信")
    public R sendMsm(@PathVariable String phone)
    {
       //从redis获取验证码,如果可以获取到直接返回
       String code = redisTemplate.opsForValue().get(phone);
        if(!StringUtils.isEmpty(code))
       {
          return R.ok();
       }
        //生成随机值,传递阿里云进行发送
        String random = RandomUtil.getFourBitRandom();
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("code",random);
        //调用service发送短信
        boolean isSend= msmService.send(hashMap,phone);
        if(isSend)
        {
            //发送成功,把验证码放到redis,并设置有效时间
           redisTemplate.opsForValue().set(phone,random,5, TimeUnit.MINUTES);
            return R.ok();
        }
        else
        {
            return R.error().message("短信发送失败");
        }
    }
}

3.3.5service

@Service
public class MsmServiceImpl implements MsmService {
    //发送短信
    @Override
    public boolean send(HashMap<String, Object> hashMap, String phone) {
        //由于短信服务开通不成功,所有返回true
        return true;
    }
}

4.登录接口

4.1创建登录的模块

4.1.1创建service-Ucenter模块

在service模块下创建子模块service-ucenter

4.1.2.创建数据库,用代码生成器生成代码

数据库

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='会员表';

代码生成器

 @Test
    public void run() {

        // 1、创建代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 2、全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir("D:\\尚硅谷在线教育项目\\在线教育--谷粒学院\\项目源码\\day10\\后端代码\\后端代码\\guli_parent\\service\\service-ucenter" + "/src/main/java");

        gc.setAuthor("smile");
        gc.setOpen(false); //生成后是否打开资源管理器
        gc.setFileOverride(false); //重新生成时文件是否覆盖

        //UserServie
        gc.setServiceName("%sService");	//去掉Service接口的首字母I

        gc.setIdType(IdType.ID_WORKER_STR); //主键策略
        gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
        gc.setSwagger2(true);//开启Swagger2模式

        mpg.setGlobalConfig(gc);

        // 3、数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("199866");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 4、包配置
        PackageConfig pc = new PackageConfig();
//        pc.setModuleName("educenter"); //模块名
        //包  com.atguigu.eduservice
        pc.setParent("com.blb");
        //包  com.atguigu.eduservice.controller
        pc.setController("controller");
        pc.setEntity("domain");
        pc.setService("service");
        pc.setMapper("mapper");
        mpg.setPackageInfo(pc);

        // 5、策略配置
        StrategyConfig strategy = new StrategyConfig();

        strategy.setInclude("ucenter_member");

        strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
        strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀

        strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
        strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作

        strategy.setRestControllerStyle(true); //restful api风格控制器
        strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符

        mpg.setStrategy(strategy);


        // 6、执行
        mpg.execute();
    }

4.1.3 Controller

@RestController
@RequestMapping("/eudcenter/member")
@CrossOrigin
public class UcenterMemberController {

    @Autowired
    private UcenterMemberService memberService;

    //登录
    @PostMapping("/logign")
    public R loginUser(@RequestBody UcenterMember ucenterMember)
    {
        //调用service实现登录返回token值
        String token= memberService.login(ucenterMember);
        return R.ok().data("token",token);
    }
    
}

4.1.4service

@Service
public class UcenterMemberServiceImpl extends ServiceImpl<UcenterMemberMapper, UcenterMember> implements UcenterMemberService {

    @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 member1 = baseMapper.selectOne(wrapper);
        //判断该手机号的用户是否存在
        if(member1==null)
        {
            throw new GuliException(20001,"该用户不存在");
        }

        //判断密码
        //因为存储到数据库的密码肯定是加密的  将输入的密码进行加密然后在与数据库的密码进行比较
        //加密方式 MD5加密
        if(!MD5.encrypt(password).equals(member1.getPassword()))
        {
            throw new GuliException(20001,"密码错误");
        }
         //判断用户是否禁用
        if(member1.getIsDisabled())
        {
            throw  new GuliException(20001,"登录失败");
        }
        //登录成功 生成token
        String jwtToken = JwtUtils.getJwtToken(member1.getId(), member1.getNickname());
        return jwtToken;

    }
}

5.用户注册

5.1 创建实体类,封装注册数据,包含验证码属性

@ApiModel("用户注册")
@Data
public class RegisterVo {

    @ApiModelProperty(value = "昵称")
    private String nicknanme;

    @ApiModelProperty(value = "手机号")
    private String mobile;

    @ApiModelProperty(value = "密码")
    private String password;

    @ApiModelProperty(value = "验证码")
    private String code;
}

5.2用户注册的接口

//注册
    @PostMapping("/register")
    @ApiOperation("用户注册")
    public R register(@RequestBody RegisterVo registerVo)
    {
        memberService.register(registerVo);
        return R.ok();
    }

5.3用户注册service

 //用户注册
    @Override
    public void register(RegisterVo registerVo) {

        if(StringUtils.isEmpty(registerVo.getCode())||StringUtils.isEmpty(registerVo.getMobile())||StringUtils.isEmpty(registerVo.getPassword())||StringUtils.isEmpty(registerVo.getNicknanme()))
        {
            throw new GuliException(20001,"该用户不存在");
        }
        if(!registerVo.getCode().equals("111111"))
        {
            throw new GuliException(20001,"验证码错误");
        }
        //判断手机号是否重复
        QueryWrapper<UcenterMember> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("mobile",registerVo.getMobile());
        Integer count = baseMapper.selectCount(queryWrapper);
        if(count>0)
        {
            throw new GuliException(20001,"该手机账户以存在");
        }
        UcenterMember ucenterMember = new UcenterMember();
        ucenterMember.setMobile(registerVo.getMobile());
        ucenterMember.setNickname(registerVo.getNicknanme());
        ucenterMember.setPassword(MD5.encrypt(registerVo.getPassword()));
        ucenterMember.setIsDisabled(false);
        baseMapper.insert(ucenterMember);
    }

5.4获取用户token信息的接口

//根据Token获取用户信息
    @GetMapping("/getMemberInfo")
    public R getMemeberInfo(HttpServletRequest request)
    {
        //调用jwt类的方法,根据request对象获取头信息,返回用户id
        String memberId = JwtUtils.getMemberIdByJwtToken(request);
        //查询数据库,根据id获取用户信息
        UcenterMember member = memberService.getById(memberId);
        return R.ok().data("userInfo",member);
    }

6. 首页面登录注册页面整合

6.1安装插件

npm install element-ui
npm install vue-qriously

6.2修改配置文件 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)

6.3 整合登录和注册页面

  1. 在layouts文件夹创建登录注册布局页面sign.vue
<template>
  <div class="sign">
    <!--标题-->
    <div class="logo">
      <img src="~/assets/img/logo.png" alt="logo">
    </div>
    <!--表单-->
    <nuxt/>
  </div>
</template>
  1. 修改登录注册超链接的地址
    在layouts下面的default.vue里面修改
    在这里插入图片描述
  2. 根据路由信息创建注册页面
    . 在这里插入图片描述
  3. 注册页面同理

6.4 注册前端整合

  1. 在api中创建register.js,定义注册接口的方法
import request from '@/utils/request'

export default{
    //根据手机号发送验证码
    sendCode(){
        return request({
            url: `/edumsm/msm/send/${phone}`,
            method: 'GET'
        })
    },
    //注册用户
    registerMember(fromItem)
    {
        return request({
        url:"/eudcenter/member/register",
        method:"post",
        data: fromItem
        })
    } 
}    
  1. register.vue
<template>
  <div class="main">
    <div class="title">
      <a href="/login">登录</a>
      <span>·</span>
      <a class="active" href="/register">注册</a>
    </div>

    <div class="sign-up-container">
      <el-form ref="userForm" :model="params">

        <el-form-item class="input-prepend restyle" prop="nickname" :rules="[{ required: true, message: '请输入你的昵称', trigger: 'blur' }]">
          <div>
            <el-input type="text" placeholder="你的昵称" v-model="params.nickname"/>
            <i class="iconfont icon-user"/>
          </div>
        </el-form-item>

        <el-form-item class="input-prepend restyle no-radius" prop="mobile" :rules="[{ required: true, message: '请输入手机号码', trigger: 'blur' },{validator: checkPhone, trigger: 'blur'}]">
          <div>
            <el-input type="text" placeholder="手机号" v-model="params.mobile"/>
            <i class="iconfont icon-phone"/>
          </div>
        </el-form-item>

        <el-form-item class="input-prepend restyle no-radius" prop="code" :rules="[{ required: true, message: '请输入验证码', trigger: 'blur' }]">
          <div style="width: 100%;display: block;float: left;position: relative">
            <el-input type="text" placeholder="验证码" v-model="params.code"/>
            <i class="iconfont icon-phone"/>
          </div>
          <div class="btn" style="position:absolute;right: 0;top: 6px;width: 40%;">
            <a href="javascript:" type="button" @click="getCodeFun()" :value="codeTest" style="border: none;background-color: none">{{codeTest}}</a>
          </div>
        </el-form-item>

        <el-form-item class="input-prepend" prop="password" :rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]">
          <div>
            <el-input type="password" placeholder="设置密码" v-model="params.password"/>
            <i class="iconfont icon-password"/>
          </div>
        </el-form-item>

        <div class="btn">
          <input type="button" class="sign-up-button" value="注册" @click="submitRegister()">
        </div>
        <p class="sign-up-msg">
          点击 “注册” 即表示您同意并愿意遵守简书
          <br>
          <a target="_blank" href="http://www.jianshu.com/p/c44d171298ce">用户协议</a><a target="_blank" href="http://www.jianshu.com/p/2ov8x3">隐私政策</a></p>
      </el-form>
      <!-- 更多注册方式 -->
      <div class="more-sign">
        <h6>社交帐号直接注册</h6>
        <ul>
          <li><a id="weixin" class="weixin" target="_blank" href="http://huaan.free.idcfengye.com/api/ucenter/wx/login"><i
            class="iconfont icon-weixin"/></a></li>
          <li><a id="qq" class="qq" target="_blank" href="#"><i class="iconfont icon-qq"/></a></li>
        </ul>
      </div>
    </div>
  </div>
</template>

<script>
  import '~/assets/css/sign.css'
  import '~/assets/css/iconfont.css'

  import registerApi from '@/api/register'

  export default {
    layout: 'sign',
    data() {
      return {
        params: { //封装注册输入数据
          mobile: '',
          code: '',  //验证码
          nickname: '',
          password: ''
        },
        sending: true,      //是否发送验证码
        second: 60,        //倒计时间
        codeTest: '获取验证码'
      }
    },
    methods: {
      //注册提交的方法
      submitRegister()
      {
       registerApi.registerMember(this.params)
       .then(response=>{
         //提示注册成功
         this.$message({
           type:'success',
           message:"注册成功"
         })
         .catch(err=>{
            this.$message({
           type:'success',
           message:err
         })
         })
         //跳转登录页面
         this.$router.push({path:'/login'})
       }) 
    
      },
      //通过输入的手机号发送验证码
      getCodeFun(){
          registerApi.sendCode(this.params.mobile)
          .then(response=>{
            this.sending=false
            //调用倒计时方法
            this.timeDown()
          })
      },     
      //倒计时
       timeDown() {
        let result = setInterval(() => {
          --this.second;
          this.codeTest = this.second
          if (this.second < 1) {
            clearInterval(result);
            this.sending = true;
            //this.disabled = false;
            this.second = 60;
            this.codeTest = "获取验证码"
          }
        }, 1000);

      },
       

      checkPhone (rule, value, callback) {
        //debugger
        // if (!(/^1[34578]\d{9}$/.test(value))) {
        //   return callback(new Error('手机号码格式不正确'))
        // }
        return callback()
      }
    }
  }
</script>

6.5 登录前端整合

6.5.1. 在api创建login.js文件,定义接口地址

import request from '@/utils/request'

export default{

    //用户登录
    submitLogin(userInfo) {
        return request({
            url:`/eudcenter/member/logign`,
            method:'post',
            data:userInfo
        })
    },
    //获取用户token
    getLoginUserInfo(){
        return request({
            url:"/eudcenter/member/getMemberInfo",
            method:'get'
        })
    }

}

6.5.2 在登录页面进行方法的调用

  1. 下载插件npm install js-cookie
  2. 页面的调用
    在这里插入图片描述

6.5.3 登录和登录成功之后首页页面显示数据实现过程分析

  1. 调用登录接口返回token字符串
  2. 把第一部返回的token字符串放在cookie里面
  3. 创建前端拦截器
    判断cookie里面是否有token字符串,如果有吧token字符串放到header(请求头)
  4. 根据token值调用接口,根据token获取用户信息,为了受页面显示,把调用接口返回的用户信息放在cookie里面
  5. 首页根据cookie获取用户信息
    实现
    在login.vue调用接口
    在这里插入图片描述
    在request.js中创建拦截器
    在这里插入图片描述
    修改layouts中的default.vue页面
    显示登录之后的用户信息
    在这里插入图片描述
    用户退出,在layouts/index.vue中当用户点击退出按钮是清空cookie
    在这里插入图片描述
 //退出
    logout(){
      //情况cookie
        cookie.set('guli_token','',{domain:'localhost'})
         cookie.set('guli_ucenter','',{domain:'localhost'})
         //返回首页面
         window.location.href="/"
    }

7.代码总结

login.js

import request from '@/utils/request'

export default{

    //用户登录
    UsersubmitLogin(userInfo) {
        return request({
            url:`/eudcenter/member/logign`,
            method:'post',
            data:userInfo
        })
    },
    //获取用户token
    getLoginUserInfo(){
        return request({
            url:"/eudcenter/member/getMemberInfo",
            method:'get'
        })
    }

}

login.vue

<template>
  <div class="main">
    <div class="title">
      <a class="active" href="/login">登录</a>
      <span>·</span>
      <a href="/register">注册</a>
    </div>

    <div class="sign-up-container">
      <el-form ref="userForm" :model="user">

        <el-form-item class="input-prepend restyle" prop="mobile" :rules="[{ required: true, message: '请输入手机号码', trigger: 'blur' },{validator: checkPhone, trigger: 'blur'}]">
          <div >
            <el-input type="text" placeholder="手机号" v-model="user.mobile"/>
            <i class="iconfont icon-phone" />
          </div>
        </el-form-item>

        <el-form-item class="input-prepend" prop="password" :rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]">
          <div>
            <el-input type="password" placeholder="密码" v-model="user.password"/>
            <i class="iconfont icon-password"/>
          </div>
        </el-form-item>

        <div class="btn">
          <input type="button" class="sign-in-button" value="登录" @click="submitLogin()">
        </div>
      </el-form>
      <!-- 更多登录方式 -->
      <div class="more-sign">
        <h6>社交帐号登录</h6>
        <ul>
          <li><a id="weixin" class="weixin" target="_blank" href="http://qy.free.idcfengye.com/api/ucenter/weixinLogin/login"><i class="iconfont icon-weixin"/></a></li>
          <li><a id="qq" class="qq" target="_blank" href="#"><i class="iconfont icon-qq"/></a></li>
        </ul>
      </div>
    </div>

  </div>
</template>

<script>
  import '~/assets/css/sign.css'
  import '~/assets/css/iconfont.css'
  import cookie from 'js-cookie'
  import loginApi from '@/api/login'
  export default {
    layout: 'sign',

    data () {
      return {
        //手机号和密码
        user:{
          mobile:'',
          password:''
        },
        //用户信息
        loginInfo:{}
      }
    },

    methods: {

      //登录
      //1. 调用接口进行登录
      submitLogin(){
        loginApi.UsersubmitLogin(this.user)
        .then(response=>{
          //2. 获取token字符串f放到cookie里面
          //参数 1. cookie名称  2.参数值 参数范围
          cookie.set('guli_token',response.data.data.token,{domain:'localhost'})
          //4. 调用接口,根据token获取用户信息进行前端的显示
          loginApi.getLoginUserInfo()
          .then(response=>{
            //根据用户返回的信息放入token
            console.log(response.data.data.userInfo)
             cookie.set('guli_ucenter',JSON.stringify(response.data.data.userInfo),{domain:'localhost'})
                
            //页面跳转
            window.location.href="/"
          })
        }
        )
      },

      checkPhone (rule, value, callback) {
        //debugger
        // if (!(/^1[34578]\d{9}$/.test(value))) {
        //   return callback(new Error('手机号码格式不正确'))
        // }
        return callback()
      }
    }
  }
</script>
<style>
   .el-form-item__error{
    z-index: 9999999;
  }
</style>

register.js

import axios from 'axios'
import {MessageBox,Message} from 'element-ui'
import cookie from 'js-cookie';

//创建axios实例
const service = axios.create({
    baseURL: 'http://localhost:9001',//api的base_url
    timeout:20000 //请求超时时间
})

// http request 拦截器
service.interceptors.request.use(
  config => {
  //debugger
  //判断是否存在名为guli_token的token
  if (cookie.get('guli_token')) {
      //把获取的cookie值放到header里面
    config.headers['token'] = cookie.get('guli_token');
  }
    return config
  },
  err => {
  return Promise.reject(err);
})


// 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)   // 返回接口返回的错误信息
});
export default service

register.vue

<template>
  <div class="main">
    <div class="title">
      <a href="/login">登录</a>
      <span>·</span>
      <a class="active" href="/register">注册</a>
    </div>

    <div class="sign-up-container">
      <el-form ref="userForm" :model="params">

        <el-form-item class="input-prepend restyle" prop="nickname" :rules="[{ required: true, message: '请输入你的昵称', trigger: 'blur' }]">
          <div>
            <el-input type="text" placeholder="你的昵称" v-model="params.nickname"/>
            <i class="iconfont icon-user"/>
          </div>
        </el-form-item>

        <el-form-item class="input-prepend restyle no-radius" prop="mobile" :rules="[{ required: true, message: '请输入手机号码', trigger: 'blur' },{validator: checkPhone, trigger: 'blur'}]">
          <div>
            <el-input type="text" placeholder="手机号" v-model="params.mobile"/>
            <i class="iconfont icon-phone"/>
          </div>
        </el-form-item>

        <el-form-item class="input-prepend restyle no-radius" prop="code" :rules="[{ required: true, message: '请输入验证码', trigger: 'blur' }]">
          <div style="width: 100%;display: block;float: left;position: relative">
            <el-input type="text" placeholder="验证码" v-model="params.code"/>
            <i class="iconfont icon-phone"/>
          </div>
          <div class="btn" style="position:absolute;right: 0;top: 6px;width: 40%;">
            <a href="javascript:" type="button" @click="getCodeFun()" :value="codeTest" style="border: none;background-color: none">{{codeTest}}</a>
          </div>
        </el-form-item>

        <el-form-item class="input-prepend" prop="password" :rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]">
          <div>
            <el-input type="password" placeholder="设置密码" v-model="params.password"/>
            <i class="iconfont icon-password"/>
          </div>
        </el-form-item>

        <div class="btn">
          <input type="button" class="sign-up-button" value="注册" @click="submitRegister()">
        </div>
        <p class="sign-up-msg">
          点击 “注册” 即表示您同意并愿意遵守简书
          <br>
          <a target="_blank" href="http://www.jianshu.com/p/c44d171298ce">用户协议</a><a target="_blank" href="http://www.jianshu.com/p/2ov8x3">隐私政策</a></p>
      </el-form>
      <!-- 更多注册方式 -->
      <div class="more-sign">
        <h6>社交帐号直接注册</h6>
        <ul>
          <li><a id="weixin" class="weixin" target="_blank" href="http://huaan.free.idcfengye.com/api/ucenter/wx/login"><i
            class="iconfont icon-weixin"/></a></li>
          <li><a id="qq" class="qq" target="_blank" href="#"><i class="iconfont icon-qq"/></a></li>
        </ul>
      </div>
    </div>
  </div>
</template>

<script>
  import '~/assets/css/sign.css'
  import '~/assets/css/iconfont.css'

  import registerApi from '@/api/register'

  export default {
    layout: 'sign',
    data() {
      return {
        params: { //封装注册输入数据
          mobile: '',
          code: '',  //验证码
          nickname: '',
          password: ''
        },
        sending: true,      //是否发送验证码
        second: 60,        //倒计时间
        codeTest: '获取验证码'
      }
    },
    methods: {
      //注册提交的方法
      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()
          })
      },     
      //倒计时
       timeDown() {
        let result = setInterval(() => {
          --this.second;
          this.codeTest = this.second
          if (this.second < 1) {
            clearInterval(result);
            this.sending = true;
            //this.disabled = false;
            this.second = 60;
            this.codeTest = "获取验证码"
          }
        }, 1000);

      },
       

      checkPhone (rule, value, callback) {
        //debugger
        // if (!(/^1[34578]\d{9}$/.test(value))) {
        //   return callback(new Error('手机号码格式不正确'))
        // }
        return callback()
      }
    }
  }
</script>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值