Day2谷粒学院登录和注册、redis存短信和首页

Day2谷粒学院登录和注册、redis存短信和首页

1.登录功能
简单说说思路。谷粒学院这个地方的登录和其它地方的登录有点不一样。其它通常就是验证一下账号和密码,然后把用户信息放入session就完成了。但是这个地方还需要注意我们启动了多个服务,服务之间是需要交流的,而且每个服务有自己的session他们之间是不能够共享的。

解决方案
①session共享,也就是把session复制到所有的服务上去,问题是消耗大量的资源
②redis+cookie的组合。把session存入redis。然后cookie保存这个session存入的名称,用于去到下一个服务时候获取。
③token。也就是把用户信息变成一串编码后的字符串,然后通过地址进行传输比如 http://localhost:1111/xxx?token=xxxx这样子的。

这里的解决方案就是采用token。简单说说用在项目的步骤。
①后端接口,根据传输的电话号和密码来获取对应的用户信息。需要注意判断电话号是否存在,然后才查询对应用户信息。然后就是把用户信息通过JWT来编码成为token。

JWT其实是一种解决方案,也就是形成token的一种规则和框架。

②前端相对来说比较简单,就是获取token。然后跳转页面(通过路由)。还要把获取到的token存入cookie,但是存入cookie之间一定要变成JSON字符串。cookie域是localhost所以可以在几个本地服务之间调用。
③然后就是跳转到首页之后显示用户信息。
(1)获取对应cookie的token。
(2)调用后端接口完成对token的解析,并且返回对应用户信息,存入cookie。
(3)显示用户信息在页面。可以通过是否存在用户id来判断是否显示。

代码
default.vue

**
<template>
  <div class="in-wrap">
    <!-- 公共头引入 -->
    <header id="header">
      <section class="container">
        <h1 id="logo">
          <a href="#" title="阿昌之谷粒学院">
            <img
              src="~/assets/img/logo.png"
              width="100%"
              alt="阿昌之谷粒学院"
            />
          </a>
        </h1>
        <div class="h-r-nsl">
          <ul class="nav">
            <router-link to="/" tag="li" active-class="current" exact>
              <a>首页</a>
            </router-link>
            <router-link to="/course" tag="li" active-class="current">
              <a>课程</a>
            </router-link>
            <router-link to="/teacher" tag="li" active-class="current">
              <a>名师</a>
            </router-link>
            <router-link to="/article" tag="li" active-class="current">
              <a>文章</a>
            </router-link>
            <router-link to="/qa" tag="li" active-class="current">
              <a>问答</a>
            </router-link>
          </ul>
          <!-- / nav -->
          <ul class="h-r-login">
            <li v-if="!loginInfo.id" id="no-login">
              <a href="/login" title="登录">
                <em class="icon18 login-icon">&nbsp;</em>
                <span class="vam ml5">登录</span>
              </a>
              <a href="/register" title="注册">
                <span class="vam ml5">注册</span>
              </a>
            </li>
            <li v-if="loginInfo.id" id="is-login-one" class="mr10">
              <a id="headerMsgCountId" href="#" title="消息">
                <em class="icon18 news-icon">&nbsp;</em>
              </a>
              <q class="red-point" style="display: none">&nbsp;</q>
            </li>
            <li v-if="loginInfo.id" id="is-login-two" class="h-r-user">
              <a href="/ucenter" title>
                <img
                  :src="loginInfo.avatar"
                  width="30"
                  height="30"
                  class="vam picImg"
                  alt
                />
                <span id="userName" class="vam disIb">{{
                  loginInfo.nickname
                }}</span>
              </a>
              <a
                href="javascript:void(0);"
                title="退出"
                @click="logout()"
                class="ml5"
                >退 出</a
              >
            </li>
            <!-- /未登录显示第1 li;登录后显示第2,3 li -->
          </ul>

          <aside class="h-r-search">
            <form action="#" method="post">
              <label class="h-r-s-box">
                <input
                  type="text"
                  placeholder="输入你想学的课程"
                  name="queryCourse.courseName"
                  value
                />
                <button type="submit" class="s-btn">
                  <em class="icon18">&nbsp;</em>
                </button>
              </label>
            </form>
          </aside>
        </div>
        <aside class="mw-nav-btn">
          <div class="mw-nav-icon"></div>
        </aside>
        <div class="clear"></div>
      </section>
    </header>
    <!-- /公共头引入 -->

    <nuxt />

    <!-- 公共底引入 -->
    <footer id="footer">
      <section class="container">
        <div class>
          <h4 class="hLh30">
            <span class="fsize18 f-fM c-999">友情链接</span>
          </h4>
          <ul class="of flink-list">
            <li>
              <a href="http://www.atguigu.com/" title="阿昌" target="_blank"
                >阿昌</a
              >
            </li>
          </ul>
          <div class="clear"></div>
        </div>
        <div class="b-foot">
          <section class="fl col-7">
            <section class="mr20">
              <section class="b-f-link">
                <a href="#" title="关于我们" target="_blank">关于我们</a>|
                <a href="#" title="联系我们" target="_blank">联系我们</a>|
                <a href="#" title="帮助中心" target="_blank">帮助中心</a>|
                <a href="#" title="资源下载" target="_blank">资源下载</a>|
                <span>服务热线:18757750375(温州) 17376597290(杭州)</span>
                <span>Email:info@atguigu.com</span>
              </section>
              <section class="b-f-link mt10">
                <span>©2021阿昌之谷粒学院</span>
              </section>
            </section>
          </section>
          <aside class="fl col-3 tac mt15">
            <section class="gf-tx">
              <span>
                <img src="~/assets/img/wx-icon.png" alt />
              </span>
            </section>
            <section class="gf-tx">
              <span>
                <img src="~/assets/img/wb-icon.png" alt />
              </span>
            </section>
          </aside>
          <div class="clear"></div>
        </div>
      </section>
    </footer>
    <!-- /公共底引入 -->
  </div>
</template>
<script>
import "~/assets/css/reset.css";
import "~/assets/css/theme.css";
import "~/assets/css/global.css";
import "~/assets/css/web.css";
import cookie from "js-cookie";
import loginApi from "@/api/login"
export default {
  data() {
    return {
      loginInfo: {
        id: "",
        age: "",
        avatar: "",
        mobile: "",
        nickname: "",
        sex: "",
      },
      token: "",
    };
  },
  created() {
    //1.获取token
    this.token=this.$route.query.token;
    //2.判断token是否存在,存在就开启微信登录
    if(this.token){
      this.wxLogin();
    }
    this.getUserInfo();
  },
  methods: {
    //微信登录
    wxLogin(){
        //3.把token放入cookie里面
       cookie.set("guli_token",this.token,{domain:"localhost"});
       //4.调用后端接口解析token
       loginApi.getUserInfo()
       .then(response=>{
         //1.返回用户信息
         this.loginInfo=response.data.data.userInfo;
         alert(loginInfo.avatar)
         var userStr=JSON.stringify(loginInfo);
         //2.设置到cookie
         cookie.set("guli_ucenter",userStr,{domain:"localhost"});
       })
    }
    ,
    //退出功能
    logout(){
      cookie.set("guli_token","",{domain:"localhost"})
      cookie.set("guli_ucenter","",{domain:"localhost"})
      window.location.href = "/";
    }
    ,
    //获取cookie并显示
    getUserInfo() {
      if(!cookie.get("guli_ucenter")){
        return ;
      }
      var userStr = cookie.get("guli_ucenter");
      console.log("展示")
      console.log(cookie.get("guli_ucenter"));
      //字符串转换成JSON
      if (userStr) {
        this.loginInfo = JSON.parse(userStr);
      }
      // this.token=cookie.get("guli_token");
    },
  },
};
</script>

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 loginApi from "@/api/login"
import cookie from "js-cookie";
export default {
  layout: "sign",
  data() {
    return {
      user: {
        //封装用于登录的用户对象
        mobile: "",
        password: "",
      },
      //用于获取接口传来的token中的对象
      loginInfo: {},
    };
  },
  methods: {
      
      submitLogin(){
          loginApi.login(this.user)
          .then(response=>{
              //1.提示
            this.$message({
                type:"success",
                message:"登录成功"
            })
              //第一步获取令牌
            response.data.data.token;
            //第二步把令牌赋值给cookie
            cookie.set("guli_token",response.data.data.token,{domain:"localhost"})
            //第四步就是调用获取userInfo,其实就是解析tokenw
            loginApi.getUserInfo()
            .then(response=>{
                // alert("我执行了")
                //获取info
                this.loginInfo=response.data.data.userInfo;
                
                //放进cookie
                cookie.set("guli_ucenter",JSON.stringify(this.loginInfo),{domain:"localhost"})
                var userStr=cookie.get("guli_ucenter")
                console.log(JSON.parse(userStr));
                //跳转到首页
                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>

ucenterController

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

    @Autowired
    UcenterMemberService ucenterMemberService;

    //1.登录
    @PostMapping("login")
    public R login(@RequestBody UcenterMember member){
        //2.返回对应的token信息
        String token=ucenterMemberService.login(member);

        return R.ok().data("token",token);
    }

    //3.getToken解析成用户信息
    @GetMapping("getUserInfo")
    public R getUserInfo(HttpServletRequest request){
        String memberId = JwtUtils.getMemberIdByJwtToken(request);
        UcenterMember member = ucenterMemberService.getById(memberId);
        return R.ok().data("userInfo",member);
    }

    //2.注册
    @PostMapping("register")
    public R register(@RequestBody RegisterVo registerVo){
        log.info("我注册了");
        ucenterMemberService.register(registerVo);
        return R.ok();
    }

    

}

2.注册功能(上面代码也涉及到controller)
注册功能包括了短信发送,倒计时。注册添加用户功能。相对来说比较简单。简单讲讲思路
①后端写发送短信验证码,验证码自己生成,然后调用短信服务api进行发送比较简单
②然后就是后端还要接收注册对象,完成新用户添加。可以用VO对象来存储这个注册对象,然后通过utils来转移属性到EduUcenter上去。
③接着就是前端写好调用的api接口,也就是js文件。然后就是页面写好调用发送验证码,注册的方法调用。(js文件就不提供。)

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: {
      //倒计时
    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);
    },
      getCodeFun(){
          registerApi.sendMsm(this.params.mobile)
          .then(response=>{
              //1.提示
              this.$message({
                  type:"success",
                  message:"发送成功"
              })
              //2.不能够点击验证码
            this.sending=false
             this.timeDown();
          })
      }
      ,
      //注册
      submitRegister(){
          registerApi.register(this.params)
          .then(response=>{
              //1.提示
             this.$message({
                  type:"success",
                  message:"注册成功"
              })
              //2.跳转
              this.$router.push({path:"/login"})
          })
      }
      ,
    checkPhone(rule, value, callback) {
      //debugger
      if (!/^1[34578]\d{9}$/.test(value)) {
        return callback(new Error("手机号码格式不正确"));
      }
      return callback();
    },
  },
};
</script>

短信发送
service(注释那段,前面那段是因为我没有注册成功,我用的是别的短信服务)

@Service
public class MsmServiceImpl implements MsmService {
    @Override
    public boolean sendMsm(Map<String, Object> params, String phone) {
        if (StringUtils.isEmpty(phone))return false;


        System.out.println("我要发送了");
        return true;
        //参数还没有修改
        //参数1:地域节点
        //参数2:AccessKey ID
        //参数3:AccessKey Secret
//        DefaultProfile profile = DefaultProfile.getProfile("default", "id", "secret");
//        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","xxxx");//申请阿里云短信服务的【模版中的 模版CODE】
//
//        //要求传递的code验证码为jason格式,可以使用JSONObject.toJSONString()将map转为json格式
//        request.putQueryParameter("TemplateParam", JSONObject.toJSONString(params));
//
//        //最终发送
//        try {
//            CommonResponse response = client.getCommonResponse(request);
//            return response.getHttpResponse().isSuccess();
//        } catch (ClientException e) {
//            e.printStackTrace();
//            return false;
//        }

    }
}

controller

@RestController
@RequestMapping("/edumsm/msm")
@CrossOrigin
@Api(description = "短信发送服务")
@Slf4j
public class MsmController {
    @Autowired
    MsmService msmService;

    @Autowired
    RedisTemplate<String,String> redisTemplate;

    @GetMapping("sentMes/{phone}")
    public R sendMessage(@PathVariable("phone")String phone){
        log.info("发送短信");
        //0.先从redis上取验证码
        String code = redisTemplate.opsForValue().get(phone);
        //2.如果不为空就直接返回
        if(!StringUtils.isEmpty(code)){
            return R.ok();
        }


        //1.获取code
        code = RandomUtil.getFourBitRandom();

        //2.创建map
        Map<String,Object> params=new HashMap<>();
        params.put("code",code);
        //3.调用service方法
       boolean isSend= msmService.sendMsm(params,phone);
        if(isSend){
            //存入redis,5分钟之后失效
            redisTemplate.opsForValue().set(phone,code,5, TimeUnit.MINUTES);
            return R.ok();
        }else{
            return R.error();
        }

    }

}

3.redis存储短信和首页
存储短信其实就是,发送的时候顺便redisTemplate存储验证码。首页呢就更简单了,只需要给需要存储的数据上面的方法加上@Cacheable就能够存储了。通常是放在service上面。但是别忘了需要把redis的组件加入到spring,并且开启cache服务。
config

@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

首页数据service(举个例子)

 @Cacheable(value = "course",key = "'selectCourseList'")
    @Override
    public List<EduCourse> getAllCourseInfo() {
        QueryWrapper<EduCourse> courseQueryWrapper=new QueryWrapper<>();
        courseQueryWrapper.orderByDesc("id");
        courseQueryWrapper.last("limit 8");
        List<EduCourse> courseList = this.list(courseQueryWrapper);
        return courseList;
    }

总结:
①存储用户的时候还要加密,对比的时候先用MD5加密之后再对比。
②优化网站访问速率可以用redis存入高频数据,利用注解@Cacheable(原理了解还不够深入)
③登录的功能步骤要么token要么redis,需要比较熟练。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值