• 2022年最新《谷粒学院开发教程》:8 - 前台登录功能




    一、整合JWT令牌

    1、在common_utils模块中添加jwt工具依赖

    <dependencies>
        <!-- JWT-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>
    </dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2、资料复制JwtUtils和MD5工具类到该模块


    二、整合阿里云短信服务

    2.1、新建短信微服务

    1、在service模块下创建子模块service_msm

    2、配置类

    # 服务器端口
    server.port=8005
    # 服务名
    spring.application.name=service-msm
    #返回json的全局时间格式
    spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
    spring.jackson.time-zone=GMT+8
    # nacos注册中心
    spring.cloud.nacos.discovery.server-addr=120.76.55.55:8848
    #配置mapper xml文件的路径
    mybatis-plus.mapper-locations=/mapper/*.xml
    #mybatis日志
    mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    # redis
    spring.redis.host=120.76.55.55
    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
    # mysql数据库连接
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://120.76.55.55:3306/guli?useSSL=false&useUnicode=true&characterEncoding=utf-8&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
    spring.datasource.username=root
    spring.datasource.password=root
    
    • 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

    3、启动类

    @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
    @ComponentScan("com.laptoy")
    public class Service_edu_Main8005 {
        public static void main(String[] args) {
            SpringApplication.run(Service_edu_Main8005.class, args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.2、开通阿里云短信服务

    1、开通短信服务

    2、添加测试手机号 签名名称和模板Code为下述

    在这里插入图片描述


    2.3、编写发送短信接口

    1、资料复制 生成随机数的工具类RandomUtils 放到common_utils模块

    2、在service-msm的pom中引入依赖

    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
        </dependency>
    </dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3、编写控制层

    @RestController
    @RequestMapping("/msmservice/msm")
    @CrossOrigin
    public class MsmController {
    
        @Autowired
        private MsmService msmService;
    
        @Autowired
        private RedisTemplate<String, String> redisTemplate;
    
        //发送短信的方法
        @GetMapping("/send/{phone}")
        public R sendMsm(@PathVariable String phone) {
            //从redis获取验证码,如果能获取,直接返回
            String code = redisTemplate.opsForValue().get(phone);
            if (!StringUtils.isEmpty(code)) {
                return R.ok();
            }
    
            //获取不到就阿里云发送
            //生成随机值,并传递给阿里云短信,让他转发给手机
            code = RandomUtils.getSixBitRandom();
            HashMap<String, Object> map = new HashMap<>();
            map.put("code", code);
    
            //调用service中发送短信的方法
            boolean isSend = msmService.sendMsm(map, phone);
            if (isSend) {
                //如果发送成功,把发送成功的code验证码保存到redis中,并设置有效时间,设置5分钟过期
                redisTemplate.opsForValue().set(phone, code, 5, TimeUnit.MINUTES);
                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
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    3、业务层

    @Service
    public class MsmServiceImpl implements MsmService {
    
        //发送短信的方法
        @Override
        public boolean sendMsm(HashMap<String, Object> map, String phone) {
            if (StringUtils.isEmpty(phone)) return false;
            //参数1:地域节点
            //参数2:AccessKey ID
            //参数3:AccessKey Secret
            DefaultProfile profile = DefaultProfile.getProfile("default", "LTAI5tL5FrVJBuQadij4KRvJ", "Xs7dHUvxCdHLd0K5iFK7NWEbdUN7GG");
            DefaultAcsClient client = new DefaultAcsClient(profile);
    
            //设置相关固定参数
            CommonRequest request = new CommonRequest();
            //request.setProtocol(ProtocolType.HTTPS);
            request.setSysMethod(MethodType.POST); //提交方式,默认不能改
            request.setSysDomain("dysmsapi.aliyuncs.com");//请求阿里云哪里,默认不能改
            request.setSysVersion("2017-05-25");//版本号
            request.setSysAction("SendSms");//请求哪个方法
    
            //设置发送相关参数
            request.putQueryParameter("PhoneNumbers", phone);//设置要发送的【手机号】
            request.putQueryParameter("SignName", "阿里云短信测试");//申请阿里云短信服务的【签名名称】
            request.putQueryParameter("TemplateCode", "SMS_154950909");//申请阿里云短信服务的【模版中的 模版CODE】
    
            //要求传递的code验证码为jason格式,可以使用JSONObject.toJSONString()将map转为json格式
            request.putQueryParameter("TemplateParam", JSONObject.toJSONString(map));
    
            //最终发送
            try {
                CommonResponse response = client.getCommonResponse(request);
                return response.getHttpResponse().isSuccess();
            } catch (ClientException 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
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    三、用户登录注册接口

    3.1、搭建微服务

    1、在service模块下创建子模块 service_ucenter

    2、资料 脚本guli_ucenter.sql 脚本生成数据

    3、逆向生成代码

    gc.setOutputDir("D:\\MyCode\\IdeaCode\\project\\gulicollege\\guli_parent\\service\\service_ucenter" + "/src/main/java"); //输出目录
    
    pc.setModuleName("ucenter"); //模块名
    
    strategy.setInclude("ucenter_member");//根据数据库哪张表生成,有多张表就加逗号继续填写
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4、配置文件

    # 服务端口
    server.port=8006
    # 服务名
    spring.application.name=service-ucenter
    # mysql数据库连接
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://120.76.55.55:3306/guli?useSSL=false&useUnicode=true&characterEncoding=utf-8&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
    spring.datasource.username=root
    spring.datasource.password=00000
    # redis
    spring.redis.host=120.76.55.55
    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=/mapper/*.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

    5、启动类

    @SpringBootApplication
    @ComponentScan("com.laptoy")
    @MapperScan("com.laptoy.ucenter.mapper")
    public class Service_edu_Main8006 {
        public static void main(String[] args) {
            SpringApplication.run(Service_edu_Main8006.class,args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    6、实体类添加默认填充时间

    public class Member implements Serializable {
        @ApiModelProperty(value = "创建时间")
        @TableField(fill = FieldFill.INSERT)
        private Date gmtCreate;
    
        @ApiModelProperty(value = "更新时间")
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private Date gmtModified;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3.2、创建登录和注册接口

    1、创建LoginVo和RegisterVo用于数据封装

    //登录对象
    @Data
    public class LoginVo {
        private String mobile;
        private String password;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    @Data
    public class RegisterVo {
        private String nickname;
        private String mobile;
        private String password;
        private String code;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2、控制层

    @RestController
    @RequestMapping("/ucenter/member")
    @CrossOrigin
    public class MemberController {
        @Autowired
        MemberService memberService;
    
        //登录
        @PostMapping("/login")
        public R login(@RequestBody LoginVo loginVo) {
            //返回token,使用jwt生成
            String token = memberService.login(loginVo);
            return R.ok().data("token", token);
        }
    
        //注册
        @PostMapping("/register")
        public R register(@RequestBody RegisterVo registerVo) {
            memberService.register(registerVo);
            return R.ok();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3、业务层

    @Service
    public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member> implements MemberService {
    
        @Autowired
        MemberMapper memberMapper;
    
        @Autowired
        StringRedisTemplate redisTemplate;
    
        //登录的方法
        @Override
        public String login(LoginVo loginVo) {
            //获取手机号和密码
            String mobile= loginVo.getMobile();
            String password = loginVo.getPassword();
            //判断输入的手机号和密码是否为空
            if (StringUtils.isEmpty(password) || StringUtils.isEmpty(mobile)) {
                throw new LaptoyException(20001, "手机号或密码为空");
            }
    
            //判断手机号是否正确
            QueryWrapper<Member> wrapper = new QueryWrapper<>();
            wrapper.eq("mobile", mobile);
            Member ucenterMember = baseMapper.selectOne(wrapper);
            if (ucenterMember == null) {
                throw new LaptoyException(20001, "手机号不存在");
            }
    
            //判断密码是否正确
            // MD5加密是不可逆性的,不能解密,只能加密
            //将获取到的密码经过MD5加密与数据库比较
            if (!MD5.encrypt(password).equals(ucenterMember.getPassword())) {
                throw new LaptoyException(20001, "密码不正确");
            }
    
            //判断用户是否禁用
            if (ucenterMember.getIsDisabled()) {
                throw new LaptoyException(20001, "用户被禁用");
            }
    
            //生成jwtToken
            String token = JwtUtils.getJwtToken(ucenterMember.getId(), ucenterMember.getNickname());
    
            return token;
    
        }
    
        //注册的方法
        @Override
        public void register(RegisterVo registerVo) {
            // 获取前端传来的数据
            String nickname = registerVo.getNickname(); //昵称
            String code = registerVo.getCode(); //验证码
            String mobile = registerVo.getMobile(); //手机号
            String password = registerVo.getPassword(); //密码
    
            // 非空判断
            if (StringUtils.isEmpty(nickname) || StringUtils.isEmpty(code) || StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {
                throw new LaptoyException(20001, "传来的数据有空值,注册失败");
            }
    
            // 判断验证码
            // 获取redis验证码,根据手机号获取
            String redisCode = redisTemplate.opsForValue().get(mobile);
            if (!code.equals(redisCode)) {
                throw new LaptoyException(20001, "注册失败");
            }
    
            // 手机号不能重复
            QueryWrapper<Member> wrapper = new QueryWrapper<>();
            wrapper.eq("mobile", mobile);
            Integer count = baseMapper.selectCount(wrapper);
            if (count >= 1) {
                throw new LaptoyException(20001, "手机号重复,注册失败");
            }
    
            // 数据添加到数据库中
            Member member = new Member();
            member.setPassword(MD5.encrypt(password));//密码加密
            member.setMobile(mobile);
            member.setNickname(nickname);
            member.setIsDisabled(false);//用户不禁用
            member.setAvatar("https://img-blog.csdnimg.cn/480b7a82bddb4f6a9b6942c88db18d85.png?imageView2/1/w/80/h/80");
            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
    • 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

    3.3、创建接口根据token获取用户信息

    在UcenterMemberController中创建方法

    //根据token获取用户信息
    @GetMapping("/getUserInfoForJwt")
    public R getUserInfoForJwt(HttpServletRequest request) {
        //调用jwt工具类里面的根据request对象,获取头信息,返回用户id
        String id = JwtUtils.getMemberIdByJwtToken(request);
        //查询数据库,根据用户id,获取用户信息
        Member member = memberService.getById(id);
        return R.ok().data("userInfo", member);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    四、用户登陆注册前端

    4.1、安装插件

    1、安装element-ui 和 vue-qriously

    npm install element-ui
    npm install vue-qriously
    
    • 1
    • 2

    2、修改配置文件 plugins/nuxt-swiper-plugin.js,使用插件

    import Vue from 'vue'
    import VueAwesomeSwiper from '../node_modules/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

    3、创建布局给登录注册使用:layouts/sign.vue

    <template>
      <div class="sign">
        <!--标题-->
        <div class="logo">
          <img src="~/assets/img/logo.png" alt="logo" />
        </div>
        <!--表单-->
        <nuxt />
      </div>
    </template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    使用的时候在对应页面指定布局,默认不指定就是用的default.vue

    在这里插入图片描述

    4、修改 layouts/default.vue 里的登录和注册地址,让其指向pages目录下对应的vue

    在这里插入图片描述


    4.2、用户注册功能

    1、api/register.js

    import request from '@/utils/request'
    
    export default {
        //根据手机号码发送短信
        getMobile(mobile) {
            return request({
                url: `/msmservice/msm/send/${mobile}`,
                method: 'get'
            })
        },
        //用户注册
        register(formItem) {
            return request({
                url: `/ucenter/member/register`,
                method: 'post',
                data: formItem
            })
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2、pages/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: {
        //通过输入的手机号,发送验证码
        getCodeFun() {
          //sending = false
          //his.sending原为true,请求成功,!this.sending == true,主要是防止有人把disabled属性去掉,多次点击;
          if (!this.sending) return;
          //debugger
          // prop 换成你想监听的prop字段
          this.$refs.userForm.validateField("mobile", (errMsg) => {
            if (errMsg == "") {
              registerApi.getMobile(this.params.mobile).then((res) => {
                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);
        },
        //注册提交的方法
        submitRegister() {
          this.$refs["userForm"].validate((valid) => {
            if (valid) {
              registerApi.register(this.params).then((response) => {
                //提示注册成功
                this.$message({
                  type: "success",
                  message: "注册成功",
                });
                this.$router.push({ path: "/login" });
              });
            }
          });
        },
        checkPhone(rule, value, callback) {
          //debugger
          if (!/^1[34578]\d{9}$/.test(value)) {
            return callback(new Error("手机号码格式不正确"));
          }
          return callback();
        },
      },
    };
    </script>
    
    • 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
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151

    4.3、用户登录功能

    1、api/login.js

    import request from '@/utils/request'
    export default {
        //登录
        submitLogin(userInfo) {
            return request({
                url: `/ucenter/member/login`,
                method: 'post',
                data: userInfo
            })
        },
        //根据token获取用户信息
        getLoginInfo() {
            return request({
                url: `/ucenter/member/getUserInfoForJwt/`,
                method: 'get',
                // headers: {'token': cookie.get('guli_token')}
            })
            //headers: {'token': cookie.get('guli_token')}
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2、安装 js-cookie 插件

    npm install js-cookie
    
    • 1

    3、pages/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: "",
          },
          //用于获取接口传来的token中的对象
          loginInfo: {},
        };
      },
      methods: {
        submitLogin() {
          loginApi.submitLogin(this.user).then((response) => {
            if (response.data.success) {
              //把token存在cookie中、也可以放在localStorage中
              //参数1:cookie名称,参数2:具体的值,参数3:作用范围
              cookie.set("guli_token", response.data.data.token, {
                domain: "localhost",
              });
              //登录成功根据token获取用户信息
              loginApi.getLoginInfo().then((response) => {
                this.loginInfo = response.data.data.userInfo;
                //将用户信息记录cookie
                cookie.set("guli_ucenter", JSON.stringify(this.loginInfo), { domain: "localhost" });
                //跳转页面
                window.location.href = "/";
                //this.$router.push({path:'/'})
              });
            }
          });
        },
        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>
    
    • 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
    • 100
    • 101
    • 102

    4.4、nginx配置

    server {
    	listen       9001;
    	server_name  localhost;
    
    	location ~ /eduservice/ {
    		proxy_pass http://localhost:8001;
    	}
    	   
    	location ~ /eduoss/ {
    		proxy_pass http://localhost:8002;
    	}
    	
    	location ~ /eduvod/ {
    		proxy_pass http://localhost:8003;
    	}
    	
    	location ~ /cmsservice/ {
    		proxy_pass http://localhost:8004;
    	}
    	
    	location ~ /msmservice/ {
    		proxy_pass http://localhost:8005;
    	}
    	
    	location ~ /ucenter/ {
    		proxy_pass http://localhost:8006;
    	}
    }
    
    • 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

    4.5、拦截器配置

    utils/request.js 用于传递token信息

    import cookie from "js-cookie";
    
    // http request 拦截器
    service.interceptors.request.use(
        config => {
            //debugger
            //判断cookie中是否有名称叫 guli_token的数据
            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
    • 14
    • 15
    • 16

    4.6、分析登录流程

    1、登录成功后调用接口获取用户信息,并将json格式的信息转为字符串存入cookie,之后跳转到index.vue页面,也就是 / (下图是login.vue)

    在这里插入图片描述

    在这里插入图片描述

    2、index.vue使用的是default.vue格式模板(下图是default.vue)

    在这里插入图片描述
    在这里插入图片描述


    五、OAuth2

    OAuth2是一种授权框架,按照一定规则生成字符串,字符串包含用户信息,但是,他不提供具体的生成规则,他是一种解决方案
    主要解决:1、开放系统间授权 ,2、分布式访问问题

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述


    六、整合微信二维码登录

    1、微信开发者平台

    2、流程

    在这里插入图片描述


    6.1、微信扫码登录

    1、配置文件固定属性

    # 微信开放平台 appid
    wx.open.app_id=wxed9954c01bb89b47
    # 微信开放平台 appsecret
    wx.open.app_secret=a7482517235173ddb4083788de60b90e
    # 微信开放平台 重定向url
    wx.open.redirect_url=http://localhost:8160/api/ucenter/wx/callback
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2、创建常量类

    @Component
    public class ConstantProperties implements InitializingBean {
    
        @Value("${wx.open.app_id}")
        private String appID;
    
        @Value("${wx.open.app_secret}")
        private String appSecret;
    
        @Value("${wx.open.redirect_url}")
        private String redirectUrl;
    
        public static String WX_APP_ID;
        public static String WX_APP_SECRET;
        public static String WX_REDIRECT_URL;
    
        @Override
        public void afterPropertiesSet() throws Exception {
            WX_APP_ID = this.appID;
            WX_APP_SECRET = this.appSecret;
            WX_REDIRECT_URL = this.redirectUrl;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    3、控制层

    @Controller //注意这里没有配置 @RestController
    @CrossOrigin
    @RequestMapping("/api/ucenter/wx")
    public class WxApiController {
    
        //生成微信扫描二维码,   %s 相当于?占位符
        @GetMapping("/login")
        public String getWxCode() {
            String baseUrl = "https://open.weixin.qq.com/connect/qrconnect"
                    + "?appid=%s"
                    + "&redirect_uri=%s"
                    + "&response_type=code"
                    + "&scope=snsapi_login"
                    + "&state=%s"
                    + "#wechat_redirect";
    
            //对redirect_uri进行URLEncoder编码
            String redirect_uri = ConstantProperties.WX_REDIRECT_URL;
            try {
                redirect_uri = URLEncoder.encode(redirect_uri, "utf-8");//参数1:待编码字符串 参数2:编码方式
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
    
            //设置 %s 占位符的参数,上面有3处
            String url = String.format(baseUrl,
                    ConstantProperties.WX_APP_ID,
                    redirect_uri,
                    "Laptoy");
    
            //请求微信地址
            return "redirect:" + url;
        }
    
    }
    
    • 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

    4、访问 http://localhost:8006/api/ucenter/wx/login

    在这里插入图片描述

    用户点击“确认登录”后,微信服务器会向谷粒学院的业务服务器发起回调,因此接下来我们需要开发回调controller


    6.2、登录成功回调

    用于测试,实际开发中,只需要修改下面的redirect_url的值指定跳转即可

    这里是尚硅谷为了让我们测试到效果,所以指定了跳转到本地的8150端口的/api/ucenter/wx/callback路径

    所以根据这个,下面就在controller中写一个这样子的方法用于扫码后跳转测试

    1、修改本地端口号 和 重定向url

    server.port=8160
    
    wx.open.redirect_url=http://localhost:8160/api/ucenter/wx/callback
    
    • 1
    • 2
    • 3

    2、nginx配置

    location ~ /ucenter/ {
    	proxy_pass http://localhost:8160;
    }
    
    • 1
    • 2
    • 3

    3、测试接口

    @GetMapping("/callback")
    public String callback(String code, String state, HttpSession session) {
        //得到授权临时票据code
        System.out.println("code = " + code);
        System.out.println("state = " + state);
        return "redirect:http://localhost:3000";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4、访问 http://localhost:8160/api/ucenter/wx/login 登陆后会重定向到主页


  • 相关阅读:
    云原生路由架构探索
    什么是长轮询
    如何统一项目中包管理器的使用?
    码蹄集 - MT2320 - 跑图:简单图问题
    知识付费系统开发搭建教程,提供免费源码搭建方案
    回归预测 | MATLAB实现BP神经网络多输入多输出回归预测
    关于#python#的问题:openssl3.0.7,并且我python编译时指定到了openssl的,request等都不行提示我没有ssl模块
    淘宝客系统开发
    PyScript运行Python第三方库
    数据结构之数组练习
  • 原文地址:https://blog.csdn.net/apple_53947466/article/details/125030987