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
2.1 配置文件application.yml 103
micr-web
login-text: 【大富科技】登录验证码是:%s,3分钟内有效,请勿泄露给他人
2.2 短信实体类 103
micr-web
修改一下注册时使用的配置类即可
JdwxSmsConfig
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
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,这就意味着这个接口有两个实现类,所以我们分别需要给注册是实现类和登录实现类加上名字,方标区分
名字加上后controller中使用这两个类就要注意注入时名字的不同
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
redis中有数据
3. 登录实现111
token和jwt相关知识我写在笔记course-10中了
3.1 添加返回结果类 111
3.2 业务接口 111
micr-api
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
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
然后使用http://localhost:8000/api/v1/user/login
填好参数发送登录请求
成功
4. 完善前端 115
Header.vue
LoginView.vue 115
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
我们使用数据库中的张无忌测试,张无忌的设定是实名认证过的
点击发送验证码
张无忌登陆成功的同时我们会将他的部分用户信息和token存入localStorage中,供以后使用方便
有过实名认证的用户会直接跳转到用户中心
接着我们在使用没有实名认证的用户登陆试试
直接跳转实名认证页面
至此成功