文章目录

  • 一、使用JWT进行跨域身份验证
    • 1、传统用户身份验证
    • 2、解决方案
  • 二、JWT令牌
    • 1、访问令牌的类型
    • 2、JWT的组成
    • 3、JWT的原则
    • 4、JWT的用法
  • 三、整合JWT令牌
    • 1、在common_utils模块中添加jwt工具依赖
    • 2、创建JWT工具类
  • 四、阿里云短信服务
    • 1、开通阿里云短信服务
    • 2、添加签名管理与模板管理
  • 五、新建短信微服务
    • 1、在service模块下创建子模块service_sms
    • 2、创建controller和service代码
    • 3、配置application.properties
    • 4、创建启动类
    • 5、在service_sms的pom中引入依赖
    • 6、编写工具类
    • 7、编写controller,根据手机号发送短信
    • 8、编写service
    • 9、使用swagger进行测试
  • 六、用户登录注册接口【后端】
    • 1、在service模块下创建子模块service_ucenter
    • 2、使用代码生成器生成代码
    • 3、配置application.properties
    • 4、创建启动类
    • 5、创建Controller编写登录和注册方法
    • 6、创建service接口和实现类
    • 7、MD5工具类
    • 8、使用swagger测试
  • 七、用户登录注册【前端】
    • 1、在Nuxt环境中安装插件
    • 2、用户注册功能前端整合
      • 1、在api文件夹中定义接口
      • 2、在pages文件夹中创建注册页面,调用方法
      • 3、修改nginx配置文件的端口
      • 4、启动服务进行测试
    • 3、用户登录功能前端整合
      • 1、在api文件夹中创建登录的js文件,定义接口
      • 2、在pages文件夹中创建登录页面,调用方法
      • 3、在request.js添加拦截器,用于传递token信息
      • 4、修改layouts中的default.vue页面
    • 4、登录注册效果展示

一、使用JWT进行跨域身份验证

1、传统用户身份验证

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

  1. 用户向服务器发送用户名和密码。
  2. 验证服务器后,相关数据(如用户角色,登录时间等)将保存在当前会话中。
  3. 服务器向用户返回session_id,session信息都会写入到用户的Cookie。
  4. 用户的每个后续请求都将通过在Cookie中取出session_id传给服务器。
  5. 服务器收到session_id并对比之前保存的数据,确认用户的身份。

这种模式最大的问题是,没有分布式架构,无法支持横向扩展

2、解决方案

  1. session广播
  2. 将透明令牌存入cookie,将用户身份信息存入redis

另外一种灵活的解决方案: 使用自包含令牌,通过客户端保存数据,而服务器不保存会话数据。 JWT是这种解决方案的代表。

二、JWT令牌

1、访问令牌的类型

2、JWT的组成

典型的,一个JWT看起来如下图:

该对象为一个很长的字符串,字符之间通过"."分隔符分为三个子串。

每一个子串表示了一个功能块,总共有以下三个部分:JWT头、有效载荷和签名

JWT头

JWT头部分是一个描述JWT元数据的JSON对象,通常如下所示。

{"alg": "HS256","typ": "JWT"
}

在上面的代码中,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存。

有效载荷

有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择。

iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT

除以上默认字段外,我们还可以自定义私有字段,如下例:

{"sub": "1234567890","name": "Helen","admin": true
}

请注意,默认情况下JWT是未加密的,任何人都可以解读其内容,因此不要构建隐私信息字段,存放保密信息,以防止信息泄露。

JSON对象也使用Base64 URL算法转换为字符串保存。

签名哈希

签名哈希部分是对上面两部分数据签名,通过指定的算法生成哈希,以确保数据不会被篡改。

首先,需要指定一个密码(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用标头中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名。

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(claims), secret)

在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用"."分隔,就构成整个JWT对象。

Base64URL算法

如前所述,JWT头和有效载荷序列化的算法都用到了Base64URL。该算法和常见Base64算法类似,稍有差别。

作为令牌的JWT可以放在URL中(例如api.example/?token=xxx)。 Base64中用的三个字符是"+","/“和”=",由于在URL中有特殊含义,因此Base64URL中对他们做了替换:"=“去掉,”+“用”-“替换,”/“用”_"替换,这就是Base64URL算法。

3、JWT的原则

JWT的原则是在服务器身份验证之后,将生成一个JSON对象并将其发送回用户,如下所示。

{"sub": "1234567890","name": "Helen","admin": true
}

之后,当用户与服务器通信时,客户在请求中发回JSON对象。服务器仅依赖于这个JSON对象来标识用户。为了防止用户篡改数据,服务器将在生成对象时添加签名。

服务器不保存任何会话数据,即服务器变为无状态,使其更容易扩展。

4、JWT的用法

客户端接收服务器返回的JWT,将其存储在CookielocalStorage中。

此后,客户端将在与服务器交互中都会带JWT。如果将它存储在Cookie中,就可以自动发送,但是不会跨域,因此一般是将它放入HTTP请求的Header Authorization字段中。当跨域时,也可以将JWT被放置于POST请求的数据主体中。

三、整合JWT令牌

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

<dependencies><!-- JWT--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId></dependency>
</dependencies>

2、创建JWT工具类

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().setHeaderParam("typ", "JWT").setHeaderParam("alg", "HS256").setSubject("guli-user").setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + EXPIRE)).claim("id", id)  //设置token主体部分 ,存储用户信息.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");}
}

四、阿里云短信服务

帮助文档:https://help.aliyun.com/product/44282.html?spm=5176.10629532.0.0.38311cbeYzBm73

1、开通阿里云短信服务

2、添加签名管理与模板管理

(1)添加签名管理

选择 国内消息 - 签名管理 - 添加签名

点击添加签名,进入添加页面,填入相关信息(签名要有实际意义)

点击提交,等待审核,审核通过后可以使用.

(2)添加模板管理

选择 国内消息 - 模板管理 - 添加模板

另外由于阿里云对个人用户限制较多,可以使用腾讯云短信服务代替.申请一个微信公众号便可进行申请使用.

五、新建短信微服务

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

2、创建controller和service代码

3、配置application.properties

server: #服务端口port: 8005
spring:application: #服务名称name: service-msmjackson: #Json格式date-format: yyyy-MM-dd HH:mm:sstime-zone: GMT+8redis: #redis配置database: 0host: 192.168.174.128lettuce:pool:max-active: 20max-idle: 5max-wait: -1min-idle: 0port: 6379timeout: 1800000#nacos服务地址cloud:nacos:discovery:server-addr: 192.168.174.128:8848
#国阳云短信服务
gyyun:sms:appCode: xxxsmsSignId: xxxtemplateId: xxx

4、创建启动类

创建SmsApplication.java

@ComponentScan({"com.rg"})//扫描swagger配置
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源自动配置
public class SmsApplication {public static void main(String[] args) {SpringApplication.run(SmsApplication.class, args);}
}

5、在service_sms的pom中引入依赖

<dependencies><!--阿里云短信发送依赖--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId></dependency><!--javaMail--><dependency><groupId>javax.mail</groupId><artifactId>javax.mail-api</artifactId><version>1.5.6</version></dependency><dependency><groupId>com.sun.mail</groupId><artifactId>javax.mail</artifactId><version>1.5.3</version></dependency><dependency><groupId>net.sf.jasperreports</groupId><artifactId>jasperreports</artifactId><version>6.8.0</version></dependency>
</dependencies>

6、编写工具类

RandomUtil:随机数工具类

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>();// 生成随机数字并存入HashMapfor (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;}
}

MailUtils:发邮件工具类

/*** 发邮件工具类*/
public final class MailUtils {private static final String USER = "2422737092@qq.com"; // 发件人称号,同邮箱地址private static final String PASSWORD = "cfznjonpuhlndhfj"; // 如果是qq邮箱可以使户端授权码,或者登录密码/**** @param to 收件人邮箱* @param text 邮件正文* @param title 标题*//* 发送验证信息的邮件 */public static boolean sendMail(String to, String text, String title){try {final Properties props = new Properties();props.put("mail.smtp.auth", "true");props.put("mail.smtp.host", "smtp.qq.com");// 发件人的账号props.put("mail.user", USER);//发件人的密码props.put("mail.password", PASSWORD);// 构建授权信息,用于进行SMTP进行身份验证Authenticator authenticator = new Authenticator() {@Overrideprotected PasswordAuthentication getPasswordAuthentication() {// 用户名、密码String userName = props.getProperty("mail.user");String password = props.getProperty("mail.password");return new PasswordAuthentication(userName, password);}};// 使用环境属性和授权信息,创建邮件会话Session mailSession = Session.getInstance(props, authenticator);// 创建邮件消息MimeMessage message = new MimeMessage(mailSession);// 设置发件人String username = props.getProperty("mail.user");InternetAddress form = new InternetAddress(username);message.setFrom(form);// 设置收件人InternetAddress toAddress = new InternetAddress(to);message.setRecipient(Message.RecipientType.TO, toAddress);// 设置邮件标题message.setSubject(title);// 设置邮件的内容体message.setContent(text, "text/html;charset=UTF-8");// 发送邮件Transport.send(message);return true;}catch (Exception e){e.printStackTrace();}return false;}public static void main(String[] args) throws Exception { // 做测试用MailUtils.sendMail("2422737092@qq.com","你好啊,李哥,我正在用代码给你发送邮件哈哈哈。","测试邮件");System.out.println("发送成功");}}

7、编写controller,根据手机号发送短信

@RestController
@RequestMapping("/smsservice/sms")
@CrossOrigin
public class SmsApiController {@Autowiredprivate RedisTemplate<String,String> redisTemplate;@Autowiredprivate SmsService smsService;//阿里云发送短信@GetMapping("sendMsgByAly/{phone}")public R sendMsgByAly(@PathVariable String phone){//生成随机值,传递到阿里云进行发送String code = RandomUtil.getSixBitRandom();Map <String, Object> param = new HashMap <>();param.put("code", code);//调用service进行短信发送boolean isSend = smsService.sendMsgAly(param, phone);if(isSend){return R.ok();}else{return R.error().message("短信发送失败");}}//使用国阳云发送短信@GetMapping("sendMsgByGyy/{phone}")public R sendMsgByGyy(@PathVariable String phone){//从redis中获取验证码,如果获取则直接返回..==> 根据现实情况,用户可以多次发送.// String code = redisTemplate.opsForValue().get(phone);// if(!StringUtils.isEmpty(code)){//     return R.ok();// }//2.如果redis获取不到,进行阿里云发送//生成随机值,传递到阿里云进行发送String code = RandomUtil.getSixBitRandom();//调用service进行短信发送boolean isSend = smsService.sendMsgGyy(code, phone);if(isSend){//发送成功,把发送成功验证码放入到redis中//设置有效时间redisTemplate.opsForValue().set(phone, code, 30, TimeUnit.MINUTES);return R.ok();}else{return R.error().message("短信发送失败");}}
}

8、编写service

@Service
public class SmsServiceImpl implements SmsService {//通过阿里云发送短信(由于未申请下来,所以就先写个方法.项目中用国阳云/邮箱进行代替)@Overridepublic boolean sendMsgAly(Map <String, Object> param, String phone) {if(StringUtils.isEmpty(phone)) return false;DefaultProfile profile =DefaultProfile.getProfile("default", "LTAIq6nIPY09VROj", "FQ7UcixT9wEqMv9F35nORPqKr8XkTF");IAcsClient client = new DefaultAcsClient(profile);CommonRequest request = new CommonRequest();//request.setProtocol(ProtocolType.HTTPS);request.setMethod(MethodType.POST);request.setDomain("dysmsapi.aliyuncs.com");request.setVersion("2017-05-25");request.setAction("SendSms");request.putQueryParameter("PhoneNumbers", phone);request.putQueryParameter("SignName", "我的谷粒在线教育网站");request.putQueryParameter("TemplateCode", "templateCode");request.putQueryParameter("TemplateParam", JSONObject.toJSONString(param));try {CommonResponse response = client.getCommonResponse(request);System.out.println(response.getData());return response.getHttpResponse().isSuccess();} catch (Exception e) {e.printStackTrace();return false;}}//使用国阳云发送短信@Overridepublic boolean sendMsgGyy(String code, String phone) {try {//真正的短信发送 用于上线// SmsUtil.sendMessage(ConstantPropertiesUtil.APP_CODE,phone,code,"30",ConstantPropertiesUtil.SMS_SIGNID,ConstantPropertiesUtil.TEMPLATE_ID);//太贵了,先用邮箱代替....MailUtils.sendMail("2422737092@qq.com", "[谷粒学院] 验证码: \n"+code+"您正在进行注册,若非本人操作,请勿泄露.30分钟内有效.", "谷粒学院验证码");return true;}catch (Exception e){e.printStackTrace();return false;}}
}

9、使用swagger进行测试

http://localhost:8005/swagger-ui.html

国阳云短信如何使用,可参考 如何利用阿里云市场购买并使用短信服务

六、用户登录注册接口【后端】

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

2、使用代码生成器生成代码

(1)创建ucenter_member表

(2)利用代码生成器生成代码

3、配置application.properties

# 服务端口
server.port=8006
# 服务名
spring.application.name=service-ucenter# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/guli_edu
spring.datasource.username=root
spring.datasource.password=186259spring.redis.host=192.168.174.128
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
#最小空闲#nacos服务地址
spring.cloud.nacos.discovery.server-addr=192.168.174.128:8848#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8#配置mapper xml文件的路径
mybatis-plus.mapper-locations=classpath:com/rg/ucenterservice/mapper/xml/*.xml#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

4、创建启动类

创建UcenterMemberApplication.java

@SpringBootApplication
@ComponentScan("com.rg")
@MapperScan("com.rg.ucenterservice.mapper")
public class UcenterMemberApplication {public static void main(String[] args) {SpringApplication.run(UcenterMemberApplication.class, args);}
}

5、创建Controller编写登录和注册方法

@RestController
@RequestMapping("/ucenterservice/member")
@CrossOrigin
public class UcenterMemberController {@Autowiredprivate UcenterMemberService 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){R result = memberService.register(registerVo);return result;}//根据token获取用户信息@GetMapping("getMemberInfo")public R getMemberInfo(HttpServletRequest request){String memberId = JwtUtils.getMemberIdByJwtToken(request);UcenterMember member = memberService.getById(memberId);return R.ok().data("userInfo", member);}}

6、创建service接口和实现类

@Service
public class UcenterMemberServiceImpl extends ServiceImpl<UcenterMemberMapper, UcenterMember> implements UcenterMemberService {@Autowiredprivate RedisTemplate<String,String> redisTemplate;//用户登录@Overridepublic String login(LoginVo loginVo) {String mobile = loginVo.getMobile();String password = loginVo.getPassword();//判断用户名密码,是否为空if(StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)){throw new GuLiException(20001, "用户名或密码不能为空!");}//去数据库查询QueryWrapper <UcenterMember> wrapper = new QueryWrapper <>();wrapper.eq("mobile",mobile);UcenterMember ucenterMember = this.baseMapper.selectOne(wrapper);//判断UCenterMember是否为空if(ucenterMember==null){//没有这个手机号throw new GuLiException(20001,"用户名或密码错误!");}//判断密码是否相等if(!MD5Utils.md5(password).equals(ucenterMember.getPassword())){throw new GuLiException(20001,"用户名或密码错误!");}//判断是否被禁用if(ucenterMember.getIsDisabled()){throw new GuLiException(20001, "您的账号暂时被冻结,请联系管理员!");}//登录成功//生成token字符串,使用jwt工具类String jwtToken = JwtUtils.getJwtToken(ucenterMember.getId(), ucenterMember.getMobile());return jwtToken;}//用户注册@Overridepublic R register(RegisterVo registerVo) {String code = registerVo.getCode();String mobile = registerVo.getMobile();String nickname = registerVo.getNickname();String password = registerVo.getPassword();//判断是否为空if(StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password) || StringUtils.isEmpty(code) || StringUtils.isEmpty(nickname)){return R.error().message("数据格式不正确!!");}//判断验证码是否正确String redisCode = redisTemplate.opsForValue().get(mobile);if(!code.equals(redisCode)){return R.error().message("验证码无效或已过期,请重新发送验证码!");}//判断手机号是否已经注册QueryWrapper <UcenterMember> wrapper = new QueryWrapper <>();wrapper.eq("mobile",mobile);Integer count = this.baseMapper.selectCount(wrapper);if(count > 0){return R.error().message("手机号已被注册!");}//判断昵称是否重复wrapper = new QueryWrapper <UcenterMember>();wrapper.eq("nickname",nickname);count = this.baseMapper.selectCount(wrapper);if(count > 0){return R.error().message("昵称 昵称已被使用,换一个吧");}//进行数据插入UcenterMember member = new UcenterMember();member.setMobile(mobile);member.setNickname(nickname);member.setPassword(MD5Utils.md5(password));member.setIsDeleted(false);//用户不禁用member.setAvatar("https://guli-photos.oss-cn-hangzhou.aliyuncs.com/FatDog.jpg");this.baseMapper.insert(member);return R.ok();}}

7、MD5工具类

public class MD5Utils {/*** 使用md5的算法进行加密*/public static String md5(String plainText) {byte[] secretBytes = null;try {secretBytes = MessageDigest.getInstance("md5").digest(plainText.getBytes());} catch (NoSuchAlgorithmException e) {throw new RuntimeException("没有md5这个算法!");}String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字// 如果生成数字未满32位,需要前面补0for (int i = 0; i < 32 - md5code.length(); i++) {md5code = "0" + md5code;}return md5code;}public static void main(String[] args) {System.out.println(md5("123456"));}}

8、使用swagger测试

http://localhost:8006/swagger-ui.html

七、用户登录注册【前端】

1、在Nuxt环境中安装插件

(1)安装element-ui 和 vue-qriously插件

npm install element-ui
npm install vue-qriously

(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)

2、用户注册功能前端整合

1、在api文件夹中定义接口

register.js

import request from '@/utils/request'export default {//发送短信sendCode(phone){return request({url:`/smsservice/sms/sendMsgByGyy/${phone}`,method:`get`})},//注册的方法registerMember(formItem){return request({url:`/ucenterservice/member/register`,method:`post`,data:formItem})}
}

2、在pages文件夹中创建注册页面,调用方法

(1)在layouts创建布局页面

sign.vue

<template><div class="sign"><!--标题--><div class="logo"><img src="~/assets/img/logo.png" alt="logo"></div><!--表单--><nuxt/></div>
</template>

(2)创建注册页面

修改layouts文件夹里面default.vue页面,修改登录和注册超链接地址

在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: 45%;"><a href="javascript:" type="button" @click="getCodeFun()" :value="codeTest" style="border: none;background-color: none"><el-button type="success" round size="medium" :disabled="!sending">{{codeTest}}</el-button></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('userForm')"></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"><iclass="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(formName){this.$refs[formName].validate(valid=>{if(!valid){this.$message({type:'error',message:'请填写正确的注册信息,再进行注册!'})return false;}registerApi.registerMember(this.params).then(response=>{console.log(response);//提示登录成功this.$message({type:'success',message:'注册成功'})//跳转到登录页面this.$router.push({path:'/login'})}) //这里不用写catch了,因为外部进行了处理})},getCodeFun(){if(this.sending==false || this.params.mobile==""){return;}registerApi.sendCode(this.params.mobile).then(response=>{//禁止发送this.sending = false//倒计时this.timeDown()//提示验证码发送成功this.$message({type:'success',message:'验证码已发送'})})},timeDown() {let result = setInterval(() => {--this.second;this.codeTest = "重新发送"+this.second+"(s)"if (this.second < 1) {clearInterval(result);this.sending = true;//this.disabled = false;this.second = 60;this.codeTest = "获取验证码"}}, 1000);},checkPhone (rule, value, callback) {//debuggerif (!(/^1[34578]\d{9}$/.test(value))) {return callback(new Error('手机号码格式不正确'))}return callback()}}}
</script>

3、修改nginx配置文件的端口

4、启动服务进行测试

3、用户登录功能前端整合

1、在api文件夹中创建登录的js文件,定义接口

import request from '@/utils/request'export default {//登录的方法submitLoginUser(userInfo){return request({url:`/ucenterservice/member/login`,method:'post',data:userInfo})},//根据token获取用户信息getLoginUserInfo(){return request({url:`/ucenterservice/member/getMemberInfo`,method:'get'})}
}

2、在pages文件夹中创建登录页面,调用方法

(1)安装js-cookie插件

npm install js-cookie

(2)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://localhost:8160/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 cookie from 'js-cookie'import loginApi from '@/api/login'export default {layout: 'sign',data () {return {user:{mobile:'',password:''},loginInfo:{}}},methods: {submitLogin(){//第一步调用接口进行登录,返回token字符串loginApi.submitLoginUser(this.user).then(response=>{//第二步,获取token字符串存放到token中cookie.set('guli_token',response.data.data.token,{domain:'localhost'})//第四步 调用接口 根据token获取用户信息,为了首页面显示loginApi.getLoginUserInfo().then(response=>{this.loginInfo = response.data.data.userInfo//获取用户信息,存放到cookie里面cookie.set("guli_ucenter",JSON.stringify(this.loginInfo),{domain:'localhost'})//跳转页面window.location.href = "/"})})},checkPhone (rule, value, callback) {//debuggerif (!(/^1[34578]\d{9}$/.test(value))) {return callback(new Error('手机号码格式不正确'))}return callback()}}}
</script>
<style>.el-form-item__error{z-index: 9999999;}
</style>

登录和登录成功后首页显示具体实现过程分析:

(1) 调用接口登录返回token字符串
(2) 把第一步返回token字符串放到cookie里面
(3) 创建拦截器:判断cookie里面是否有token字符串,如果有,把token字符串放到header(请求头中)
(4) 根据token值,调用接口,根据token获取用户信息,为了首页面显示。调用接口返回用户信息放到cookie里面
(5) 从首页面显示用户信息,从第四步cookie获取用户信息

3、在request.js添加拦截器,用于传递token信息

import axios from 'axios'
import cookie from 'js-cookie'
import { MessageBox, Message } from 'element-ui'
// 创建axios实例
const service = axios.create({baseURL: 'http://localhost:9001', // api的base_urltimeout: 15000 // 请求超时时间
})//第三步 创建拦截器  http request 拦截器
service.interceptors.request.use(config => {//debuggerif (cookie.get('guli_token')) {config.headers['token'] = cookie.get('guli_token');}return config},err => {return Promise.reject(err);
})
// http response 拦截器
service.interceptors.response.use(response => {//debuggerif (response.data.code == 28004) {console.log("response.data.resultCode是28004")// 返回 错误代码-1 清除ticket信息并跳转到登录页面//debuggerwindow.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})return Promise.reject('error')}} else {return response;}}},error => {console.log('err' + error) // for debugMessage({message: error.message,type: 'error',duration: 5 * 1000})return Promise.reject(error.response)   // 返回接口返回的错误信息
});export default service

4、修改layouts中的default.vue页面

(1)显示登录之后的用户信息

<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 {token: '',loginInfo: {id: '',age: '',avatar: '',mobile: '',nickname: '',sex: ''}}},created() {//获取路径里面的token值this.token = this.$route.query.tokenif(this.token){//判断路径中是否有token值this.wxLogin()return;}this.showInfo()},methods: {//从cookie中获取用户信息showInfo(){//从cookie获取用户信息  cookie中存放的都是strlet userStr = cookie.get('guli_ucenter')console.log(userStr);if(userStr){this.loginInfo = JSON.parse(userStr)}},//退出logout(){//清空cookie值cookie.set('guli_token','',{domain:'localhost'})cookie.set('guli_ucenter','',{domain:'localhost'})//回到首页面window.location.href = "/"}}
}
</script>

(2)default.vue页面显示登录之后的用户信息

<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>

4、登录注册效果展示

  • 用户填写注册信息,填写收到的短信验证码(目前先用邮箱代替),进行注册

  • 注册表单校验功能

  • 注册成功

  • 用户登录


如果有收获!!! 希望老铁们来个三连,点赞、收藏、转发。
创作不易,别忘点个赞,可以让更多的人看到这篇文章,顺便鼓励我写出更好的博客

谷粒学院(十五)JWT | 阿里云短信服务 | 登录与注册前后端实现相关推荐

  1. 谷粒学院——Day12【整合阿里云短信服务、首页登录和注册】

    ❤ 作者主页:欢迎来到我的技术博客

  2. springboot+springsecurity+阿里云短信服务验证实现注册登录

    使用springboot+security+Aliyun短信服务实现注册登录 为了实现个人博客部分的登录注册,我采用了阿里云短信服务发送验证码,后端比对验证码的方式完成注册,现在功能还不完全,以后这个 ...

  3. 谷粒学院-阿里云短信服务

    新建短信微服务 1.在service模块下创建子模块service_msm 2.创建controller和service代码 3.配置application.properties # 服务端口 ser ...

  4. 尚医通-阿里云短信服务(二十九)

    目录: (1)前台用户系统-手机登录-阿里云短信服务介绍 (2)手机登录-整合短信服务 (3)整合短信发送服务测试 (1)前台用户系统-手机登录-阿里云短信服务介绍 现在使用阿里云完成短信服务:注册登 ...

  5. 使用阿里云短信服务API实现短信验证码以及短信服务通知

    使用阿里云短信服务API实现短信验证码以及短信服务通知 前言 一 .短信调用简要说明 二 .官方不带签名原生态测试demo 调用结果如下 三 .以上为不带模板和签名的API调用结果 下面加入签名和模板 ...

  6. 阿里云短信服务-个人账户测试短信服务

    前言 阿里的短信服务分为三块,一是阿里云短信服务,二是云通讯,三是阿里大于,不过后期都会统一到阿里云短信服务平台的,所以这里只需要学习如何使用阿里云短信服务就可以了. 如果是个人开发者,项目呢还没上线 ...

  7. 单点登录常见解决方式和阿里云短信服务

    1 单点登录(SSO) 三种常见方式: 1.1 第一种:session广播机制实现(已淘汰) 概念:就是session复制,一个模块登录后,该模块session存放用户登录信息,再把该session复 ...

  8. 阿里云短信服务——短信发送验证码

    引言 短信服务现在在很多地方都用的到,我们最多接触到的可能就是验证码的发送了:现在很多大厂都提供由短信服务对应的api,直接调用就可以轻松实现短信发送:下面我以阿里云为例,演示一下如何短信发送验证码: ...

  9. 对接阿里云短信服务(附视频教程)

    阿里云短信服务文档使用指引: https://help.aliyun.com/document_detail/59210.html B站视频教程链接: https://www.bilibili.com ...

最新文章

  1. (纪录片)统计的乐趣 The Joy of Stats (2010)
  2. virtual DOM和真实DOM的区别_让虚拟DOM和DOMdiff不再成为你的绊脚石
  3. ASP.NET MVC5+EF6+EasyUI 后台管理系统(92)-打印EasyUI 的datagrid表格
  4. linux循环脚本while循环,Shell脚本while、until循环语句简明教程
  5. tensorflow知识点
  6. 我见过的最漂亮代码---通过删除代码来实现功能的提升
  7. oracle几个网络,ORACLE网络的几个重点概念
  8. z字扫描和光栅扫描的转换_扫描转换计算机图形中的直线
  9. Fibonacci数列使用迭代器实现
  10. vue-router向子组件传递参数
  11. mysql 主主_MySQL双主(主主)架构
  12. VScode怎么查看python源码
  13. 教务信息管理系统的设计与实现
  14. 论文:Object-centric Auto-encoders and Dummy Anomalies for Abnormal Event Detection in Video阅读遇到的问题及解答
  15. The Sultan's Successors
  16. 迪拜国际学术城各所大学中国学生入学人数持续增长
  17. html reset 无效,HTML中的input type=reset标签失效(不起作用)的可能原因
  18. 计算机操作系统复习精简版
  19. 360wifi一直在启用中
  20. PaddleOCR数字仪表识别——3.paddleocr迁移学习

热门文章

  1. 35美金可以在Fiverr上让人帮你设计1个NFT卡通头像
  2. php 5.3.29 nts,为什么PHP 5.3不在支持ISAPI,为什么还要分NTS和TS版本?
  3. 数据结构--表达式括号匹配和运算
  4. UVA11400 简单DP
  5. ios设备home键 锁屏键区分
  6. Android监听锁屏键、Home键
  7. 划重点 iOS15正式发布, 全新的通知推送系统,你必须要知道
  8. iphone投屏老是显示无法连接服务器,iPhone 无法投屏到电视如何解决?
  9. C# HttpWebRequest 必须使用适当的属性或方法修改“Host”标头;必须使用适当的属性或方法修改“Date”标头。
  10. SpringBoot实现每天给对象发送情话