1.  登录界面   102、104

1.1 添加界面  102、104

<template>
  <div>
    <Header></Header>
    <div className="login-content">
      <div className="login-flex">
        <div className="login-left">
          <h3>加入动力金融网</h3>
          <p>坐享<span>{{ platInfo.historyAvgRate }}%</span>历史年化收益</p>
          <p>平台用户<span>{{ platInfo.registerUsers }}</span>位 </p>
          <p>累计成交金额<span>{{ platInfo.sumBidMoney }}</span>元</p>
        </div>
        <!---->
        <div className="login-box">
          <h3 className="login-title">欢迎登录</h3>
          <form action="" id="login_Submit">
            <div className="alert-input">
              <!--<input class="form-border user-name" name="username" type="text" placeholder="您的姓名">
              <p class="prompt_name"></p>-->
              <input type="text" className="form-border user-num" v-model="phone" @blur="checkPhone" name="mobile"
                     placeholder="请输入11位手机号">
              <div className="err">{{ phoneErr }}</div>
              <p className="prompt_num"></p>
              <input type="password" placeholder="请输入登录密码" className="form-border user-pass" v-model="password"
                     @blur="checkPassword" autocomplete name="password">
              <div className="err">{{ passwordErr }}</div>
              <p className="prompt_pass"></p>
              <div className="form-yzm form-border">
                <input className="yzm-write" type="text" v-model="code" @blur="checkCode" placeholder="输入短信验证码">
                <input className="yzm-send" type="button" value="获取验证码" id="yzmBtn" @click="requestSmsCode">
              </div>
              <div className="err">{{ codeErr }}</div>
              <p className="prompt_yan"></p>
            </div>
            <div className="alert-input-btn">
              <input type="submit" className="login-submit" value="登录">
            </div>
          </form>

        </div>

      </div>
    </div>
    <Footer></Footer>

  </div>
</template>

<script>
import Footer from "@/components/common/Footer";
import Header from "@/components/common/Header";
import {doGet} from "@/api/httpRequest";
import layx from "vue-layx";

export default {
  name: "LoginView",
  components: {
    // eslint-disable-next-line vue/no-unused-components
    Header,
    // eslint-disable-next-line vue/no-unused-components
    Footer
  },
  data() {
    return {
      platInfo: {historyAvgRate: 0.00, sumBidMoney: 0.00, registerUsers: 0},
      phone: '13812345699',
      phoneErr: '',
      password: '111aaa',
      passwordErr: '',
      code: '',
      codeErr: '',
    }
  },
  mounted() {
    doGet('/v1/plat/info').then(resp => {
      if (resp) {
        this.platInfo = resp.data.data;
      }
    })
  },
  methods: {
    checkPhone() {
      if (this.phone == '' || this.phone == undefined) {
        this.phoneErr = '请输入手机号';
      } else if (this.phone.length != 11) {
        this.phoneErr = '手机号长度不足11位';
      } else if (!/^1[1-9]\d{9}$/.test(this.phone)) {
        this.phoneErr = '手机号格式不正确'
      } else {
        this.phoneErr = '';
      }
    },
    checkPassword() {
      if (this.password == '' || this.password == undefined) {
        this.passwordErr = '请输入密码';
      } else if (this.password.length < 6 || this.password.length > 20) {
        this.passwordErr = '密码长度是6-20位';
      } else if (!/^[0-9a-zA-Z]+$/.test(this.password)) {
        this.passwordErr = '密码只能使用数字和字母';
      } else if (!/^(([a-zA-Z]+[0-9]+)|([0-9]+[a-zA-Z]+))[a-zA-Z0-9]*/.test(this.password)) {
        this.passwordErr = '密码是数字和字母的混合';
      } else {
        this.passwordErr = '';
      }
    },
    checkCode() {
      if (this.code == '' || this.code == undefined) {
        this.codeErr = '必须输入验证码';
      } else if (this.code.length != 4) {
        this.codeErr = '验证码是4位的';
      } else {
        this.codeErr = '';
      }
    },
    requestSmsCode() {
      this.checkPhone();
      if (this.phoneErr == '') {
        //调用服务器发到短信的接口  /code/login
        doGet('/v1/sms/code/login', {phone: this.phone}).then(resp => {
          if (resp && resp.data.code == 1000) {
            layx.msg('短信已经发送了', {dialogIcon: 'success', position: 'ct'});
          }
        })
      }
    }
  }
}
</script>

<style scoped>
.err {
  color: red;
  font-size: 18px;
}
</style>

2. 登录短信验证码实现后端   103

登陆开发_json

2.1 配置文件application.yml   103

micr-web

登陆开发_验证码_02

login-text: 【大富科技】登录验证码是:%s,3分钟内有效,请勿泄露给他人

2.2 短信实体类  103

micr-web

修改一下注册时使用的配置类即可

JdwxSmsConfig

登陆开发_json_03

package com.bjpowernode.front.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

//京东万象短信配置类  85
@Component
@ConfigurationProperties(prefix = "jdwx.sms")
public class JdwxSmsConfig {
    private String url;
    private String appkey;
    private String content;
    private String loginText; //登陆时的短信文本  103

    public String getLoginText() {
        return loginText;
    }

    public void setLoginText(String loginText) {
        this.loginText = loginText;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getAppkey() {
        return appkey;
    }

    public void setAppkey(String appkey) {
        this.appkey = appkey;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

2.3 登录验证码实现类  103

micr-web

2.3.1 添加登录验证码redis缓存的key

登陆开发_验证码_04

2.3.2 SmsCodeLoginImpl   103

package com.bjpowernode.front.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.bjpowernode.common.constants.RedisKey;
import com.bjpowernode.front.config.JdwxSmsConfig;
import com.bjpowernode.front.service.SmsService;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * 登录发送短信验证码   103
 */

@Service(value = "smsCodeLoginImpl")
public class SmsCodeLoginImpl implements SmsService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private JdwxSmsConfig smsConfig;

    //发送短信验证码  103
    @Override
    public boolean sendSms(String phone) {
        boolean send = false;
        // 设置短信内容
        String random  = RandomStringUtils.randomNumeric(4);
        System.out.println("登录验证码的随机数 random="+random);
        //更新content中的  %s   【大富科技】登录验证码是:%s,3分钟内有效,请勿泄露给他人
        String content  = String.format(smsConfig.getLoginText(), random);

        //使用HttpClient发送 get 请求给第三方。
        CloseableHttpClient client = HttpClients.createDefault();
        //https://way.jd.com/chuangxin/dxjk?mobile=13568813957&content=
        //【创信】你的验证码是:5873,3分钟内有效!&appkey=您申请的APPKEY
        String url = smsConfig.getUrl()+"?mobile="+phone
                        +"&content=" + content
                        +"&appkey="+smsConfig.getAppkey();
        HttpGet get  = new HttpGet(url);

        try{
            //CloseableHttpResponse response = client.execute(get);
           // if( response.getStatusLine().getStatusCode() == HttpStatus.SC_OK ){
                //得到返回的数据,json
                //String text = EntityUtils.toString(response.getEntity());
                String text="{\n" +
                        "    \"code\": \"10000\",\n" +
                        "    \"charge\": false,\n" +
                        "    \"remain\": 1305,\n" +
                        "    \"msg\": \"查询成功\",\n" +
                        "    \"result\": {\n" +
                        "        \"ReturnStatus\": \"Success\",\n" +
                        "        \"Message\": \"ok\",\n" +
                        "        \"RemainPoint\": 420842,\n" +
                        "        \"TaskID\": 18424321,\n" +
                        "        \"SuccessCounts\": 1\n" +
                        "    }\n" +
                        "}";
                //解析json
                if(StringUtils.isNotBlank(text)){
                    // fastjson
                    JSONObject jsonObject = JSONObject.parseObject(text);
                    if("10000".equals(jsonObject.getString("code"))){ //第三方接口调用成功
                        //读取result中的key:ReturnStatus
                        if("Success".equalsIgnoreCase(
                                jsonObject.getJSONObject("result").getString("ReturnStatus"))){
                            //短信发送成功
                            send  = true;

                            //把短信验证码,存到redis
                            String key = RedisKey.KEY_SMS_CODE_LOGIN + phone;
                            stringRedisTemplate.boundValueOps(key).set(random,3 , TimeUnit.MINUTES);

                        }
                    }
                }
           // }
        }catch (Exception e){
            e.printStackTrace();
        }
        return send;
    }

    //检查验证码是否有效   103
    @Override
    public boolean checkSmsCode(String phone, String code) {
        String key = RedisKey.KEY_SMS_CODE_LOGIN + phone;
        if( stringRedisTemplate.hasKey(key)){
            String querySmsCode = stringRedisTemplate.boundValueOps(key).get();
            if( code.equals(querySmsCode)){
                return true;
            }
        }
        return false;
    }


}

2.4 消费者controller  103

micr-web

提示

由于我们的注册和登录使用的接口都是SmsService,这就意味着这个接口有两个实现类,所以我们分别需要给注册是实现类和登录实现类加上名字,方标区分

登陆开发_验证码_05

登陆开发_apache_06

名字加上后controller中使用这两个类就要注意注入时名字的不同

登陆开发_验证码_07

SmsController

/**发送登录验证码短信   103*/
    @GetMapping("/code/login")
    public RespResult sendCodeLogin(@RequestParam String phone){
        RespResult result = RespResult.fail();
        if(CommonUtil.checkPhone(phone)){
            //判断redis中是否有这个手机号的验证码
            String key  = RedisKey.KEY_SMS_CODE_LOGIN + phone;
            if(stringRedisTemplate.hasKey(key)){
                result = RespResult.ok();
                result.setRCode(RCode.SMS_CODE_CAN_USE);
            } else {
                boolean isSuccess = loginSmsService.sendSms(phone);
                if( isSuccess ){
                    result = RespResult.ok();
                }
            }
        } else {
            result.setRCode(RCode.PHONE_FORMAT_ERR);
        }
        return result;
    }

测试  104

登陆开发_apache_08

登陆开发_验证码_09

登陆开发_验证码_10

redis中有数据

登陆开发_验证码_11

3. 登录实现111

token和jwt相关知识我写在笔记course-10中了

登陆开发_验证码_12

3.1 添加返回结果类  111

登陆开发_json_13

3.2 业务接口  111

micr-api

登陆开发_json_14

UserService

//用户登录  111
    User userLogin(String phone, String pword);

3.3 业务接口实现类  111

micr-dataservice

UserServiceImpl

//用户登录实现类  111
    @Transactional(rollbackFor = Exception.class)
    @Override
    public User userLogin(String phone, String password) {
        User user = null;
        if(CommonUtil.checkPhone(phone) && (password !=null && password.length() == 32)){
            //因为用户在注册时我们对其密码进行了加盐处理,所以我们要查也要加盐后查  111
            String newPassword = DigestUtils.md5Hex(password + passwordSalt);
            user = userMapper.selectLogin(phone,newPassword);
            //更新最后的登陆时间   112
            if(user!=null){
                user.setLastLoginTime(new Date());
                userMapper.updateByPrimaryKeySelective(user);
            }
        }
        return user;
    }

3.4 在mapper中定义方法  111

micr-dataservice

UserMapper

//用户登录  111
    User selectLogin(@Param("phone") String phone, @Param("loginPassword") String newPassword);

3.5 编写sql   111

micr-dataservice

UserMapper.xml

<!--用户登录  111-->
  <select id="selectLogin" resultMap="BaseResultMap">
    select <include refid="Base_Column_List"></include>
    from u_user
    where phone = #{phone} and  login_password = #{loginPassword}
  </select>

3.6 生成jwt的工具类   112

micr-web

生成jwt的密钥  112

application.yml     

#设置生成jwt的密钥  112
jwt:
  secret: 342903934cb944808920b642616b3e76

micr-common

JwtUtil   112

package com.bjpowernode.common.util;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.apache.commons.lang3.time.DateUtils;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
import java.util.UUID;

/**
 * 生成jwt的工具类   112
 */
public class JwtUtil {

    private String selfKey;

    public JwtUtil(String selfKey) {
        this.selfKey = selfKey;
    }

    //创建jwt
    public String createJwt(Map<String,Object> data, Integer minute) throws Exception{
        Date curDate = new Date();
        SecretKey secretKey = Keys.hmacShaKeyFor(selfKey.getBytes(StandardCharsets.UTF_8));
        String jwt = Jwts.builder().signWith(secretKey, SignatureAlgorithm.HS256)
                    .setExpiration(DateUtils.addMinutes(curDate,minute))
                    .setIssuedAt(curDate)
                    .setId(UUID.randomUUID().toString().replaceAll("-","").toUpperCase())
                    .addClaims(data)
                    .compact();
        return jwt;
    }
}

在启动类创建一个JwtUtil工具类的对象   112

micr-web

启动类MicrWebApplication

//创建一个JwtUtil工具类的对象  112
	@Value("${jwt.secret}")
	private String secertkey;
	@Bean
	public JwtUtil jwtUtil(){
		JwtUtil jwtUtil = new JwtUtil(secertkey);
		//返回JwtUtil工具类的对象
		return jwtUtil;
	}

micr-common

添加枚举类   113

登陆开发_验证码_15

3.7 消费者controller   111-114

micr-web

UserContorller

//用户登录,获取token-jwt  111
    @ApiOperation(value = "用户登录-获取访问token")
    @PostMapping("/login")
    public RespResult userLogin(@RequestParam String phone,
                                @RequestParam String pword,
                                @RequestParam String scode) throws Exception {
        RespResult result = RespResult.fail();
        if(CommonUtil.checkPhone(phone) && (pword != null && pword.length() == 32) ){
            //检查验证码
            if(loginSmsService.checkSmsCode(phone,scode)){
                //访问data-service
                User user = userService.userLogin(phone,pword);
                if( user != null){
                    //登录成功,生成token    112
                    Map<String, Object> data = new HashMap<>();
                    //使用用户的id生成token   113
                    data.put("uid",user.getId());
                    String jwtToken = jwtUtil.createJwt(data,120);

                    result = RespResult.ok();
                    result.setAccessToken(jwtToken);

                    //将用户的部分信息返回给前端   113
                    Map<String,Object> userInfo = new HashMap<>();
                    userInfo.put("uid",user.getId());
                    userInfo.put("phone",user.getPhone());
                    userInfo.put("name",user.getName());
                    result.setData(userInfo);
                } else {
                    result.setRCode(RCode.PHONE_LOGIN_PASSWORD_INVALID);
                }
            } else {
                result.setRCode(RCode.SMS_CODE_INVALID);
            }
        } else {
            result.setRCode(RCode.REQUEST_PARAM_ERR);
        }

        return result;

    }

测试  114

启动服务

使用postman

首先使用发送验证码http://localhost:8000/api/v1/sms/code/login

登陆开发_json_16

登陆开发_apache_17

登陆开发_json_18

然后使用http://localhost:8000/api/v1/user/login

填好参数发送登录请求

成功

登陆开发_验证码_19

4. 完善前端  115

Header.vue

登陆开发_json_20

LoginView.vue   115

登陆开发_apache_21

登陆开发_验证码_22

userLogin(){   //登录函数  115
      this.checkPhone();
      this.checkPassword();
      this.checkCode();
      if( this.phoneErr == '' && this.passwordErr == '' & this.codeErr==''){
        //发起登录请求
        let param = {
          phone:this.phone,pword:md5(this.password),scode:this.code
        }
        doPost('/v1/user/login',param).then(resp=>{
          if( resp && resp.data.code == 1000){
            //登录成功,存储数据到localStorage,存的是字符串
            window.localStorage.setItem("token",resp.data.accessToken);
            //把 json对象转为 string
            window.localStorage.setItem("userinfo", JSON.stringify(resp.data.data));
            //登录之后,如果name没有值,需要进入到实名认证页面
            //如果name有值,进入到用户中心
            if(resp.data.data.name == ''){
              //实名认证
              this.$router.push({
                path:'/page/user/realname'
              })
            } else {
              //用户中心
              this.$router.push({
                path:'/page/user/usercenter'
              })
            }
          }
        })
      }
    }

5. 测试  116

我们使用数据库中的张无忌测试,张无忌的设定是实名认证过的

点击发送验证码

登陆开发_json_23

登陆开发_验证码_24

登陆开发_apache_25

张无忌登陆成功的同时我们会将他的部分用户信息和token存入localStorage中,供以后使用方便

登陆开发_apache_26

有过实名认证的用户会直接跳转到用户中心

登陆开发_json_27

接着我们在使用没有实名认证的用户登陆试试

登陆开发_apache_28

登陆开发_验证码_29

登陆开发_apache_30

直接跳转实名认证页面

登陆开发_验证码_31

至此成功