文章目录
- 1 介绍
- 2 登录业务流程
- 3 JWT令牌
- 4 阿里云短信服务
- 5 登录功能
- 6 注册功能
- 7 根据token获取用户信息
- 8 整合首页登录和注册
1 介绍
-
登录实现流程
-
注册接口
- 整合JWT
- 整合阿里云短信服务
-
登录接口
-
注册、登录的前端实现
2 登录业务流程
-
单一服务器模式
-
使用session对象实现
-
登录成功后,将用户数据放到session
-
登录时,判断是否登录,从session获取数据,实现登录
-
session.setAttritute("user", user); session.getAttribute("user");
-
性能压力,无法扩展
-
-
-
集群部署
- 分布式
- 单点登录
-
SSO单点登录模式 single sign on
-
三种常见方式
1、session广播机制实现(基本不用了)
- session复制
2、使用cookie+redis 实现
- 1.在项目中任何一个模块进行登录,登录后将数据放到两个地方
- redis:在key:生成唯一随机值(ip、id等),在value:用户数据
- cookie:把redis里面生成的key值放到cookie中
- 2.访问项目中其他模块,发送请求时带着cookie进行发送,获取cookie值后
- 把获取到的cookie值放入redis进行查询,根据key查询,如果能查到数据,就是登录
3、使用token实现
-
令牌
-
token:按照一定的规则生成字符串,字符串可以包含用户信息 自包含令牌
-
1.在项目某个模块进行登录,登录之后,按照规则生成字符串,将用户包含在字符串里,最后返回字符串
- 把字符串通过cookie返回
- 把字符串通过地址栏返回
-
2.再访问项目其他模块,每次访问在地址栏带着生成的字符串,在访问模块里获取地址栏字符串,根据字符串获取用户信息。如果可以获取到,就是登录
-
session默认过期时间:30min
session、redis、token都可以设置过期时间
3 JWT令牌
-
JWT
-
token是按照一定规则生成字符串,包含用户信息
-
规则是怎么样的,不一定
-
一般采用通用的规则,官方规则JWT
-
JTW就是规定好的规则,使用JWT可以直接生成字符串,包含用户信息
-
-
JWT生成的字符串包含三部分
- 1 jwt头信息
- 2 有效载荷,包含主体信息(用户信息)
- 3 签名哈希(防伪标志)
1 在common中引入依赖,复制jwt工具类
common/common_utils/pom.xml
<dependencies>
<!-- JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
</dependencies>
JwtUtils.java
4 阿里云短信服务
整合阿里云短信服务,注册时发生验证码
1 在service中创建子模块service_msm
2 创建包结构,创建controller、service
3 阿里云平台操作
- 开通阿里云短信服务
- 进入控制台,点击国内消息,申请两个内容:签名管理、模板管理
- 申请模板管理
- 申请签名管理
- 注:现在有测试版,可以直接使用测试版,不申请
需求:验证码五分钟内有效
使用Redis解决验证码有效时间
controller
@RestController
@CrossOrigin
@RequestMapping("/edumsm/msm")
public class MsmController {
@Autowired
private MsmService msmService;
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 发送短信
@GetMapping("send/{phone}")
public R sendMsm(@PathVariable String phone) {
// 1. 先从redis中获取验证码,如果能获取直接返回
String code = redisTemplate.opsForValue().get(phone);
if (! StringUtils.isEmpty(code)) {
return R.ok();
}
// 2. 如果获取不到,再进行阿里云发送
// 生成随机的值,传递给阿里云发送
code = RandomUtil.getFourBitRandom();
Map<String, Object> param = new HashMap<>();
param.put("code", code);
// 调用service发送短信的方法
boolean isSend = msmService.send(param, phone);
if (isSend) {
// 发送成功,把发送成功验证码放到redis里,并设置有效时间
redisTemplate.opsForValue().set(phone, code, 5, TimeUnit.MINUTES);
return R.ok();
} else {
return R.error().message("短信发送失败");
}
}
}
service 固定写法,修改自己的参数
@Service
public class MsmServiceImpl implements MsmService {
// 发送短信
@Override
public boolean send(Map<String, Object> param, String phone) {
if (StringUtils.isEmpty(phone)) return false;
DefaultProfile profile = DefaultProfile.getProfile("default", "accessKeyId", "secret");
IAcsClient client = new DefaultAcsClient(profile);
// 设置相关固定参数
CommonRequest request = new CommonRequest();
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", "SMS_154950909");
request.putQueryParameter("TemplateParam", JSONObject.toJSONString(param)); // 验证码
// 最终发送
try {
CommonResponse response = client.getCommonResponse(request);
boolean success = response.getHttpResponse().isSuccess();
return success;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
5 登录功能
1 在service下创建子模块 service_ucenter
2 创建用户表,使用代码生成器生成相关代码
guli_ucenter.sql
ucenter_member
3 配置文件application.properties
4 启动类 UcenterApplication
@SpringBootApplication
@ComponentScan("com.mys")
@MapperScan("com.mys.educenter.mapper")
public class UcenterApplication {
public static void main(String[] args) {
SpringApplication.run(UcenterApplication.class);
}
}
5 controller 、service
controller
// 登录
@GetMapping("login")
public R loginUser(@RequestBody UcenterMember member) {
// 使用service实现登录,返回token值(jwt生成)
String token = memberService.login(member);
return R.ok().data("token", token);
}
service
// 登录
@Override
public String login(UcenterMember member) {
// 获取登录的手机号和密码
String mobile = member.getMobile();
String password = member.getPassword();
// 手机号和密码非空判断
if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {
throw new GuliException(20001, "登录失败");
}
// 判断手机号是否正确
QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
wrapper.eq("mobile", mobile);
UcenterMember mobileNumber = baseMapper.selectOne(wrapper);
// 判断查询对象是否为空
if (mobileNumber == null) { // 没有这个手机号
throw new GuliException(20001, "登录失败");
}
// 判断密码
if (! password.equals(mobileNumber.getPassword())) {
throw new GuliException(20001, "登录失败");
}
// 判断用户是否禁用
if (mobileNumber.getIsDisabled()) {
throw new GuliException(20001, "登录失败");
}
// 登录成功,使用jwt生成token字符串
String jwtToken = JwtUtils.getJwtToken(mobileNumber.getId(), mobileNumber.getNickname());
return jwtToken;
}
测试
6 注册功能
1 创建实体类,封装注册数据,包含验证码属性
entity.vo.RegisterVo
@Data
public class RegisterVo {
@ApiModelProperty(value = "昵称")
private String nickname;
@ApiModelProperty(value = "手机号")
private String mobile;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "验证码")
private String code;
}
2 在controller中创建注册方法
// 注册
@PostMapping("register")
public R registerUser(@RequestBody RegisterVo registerVo) {
memberService.register(registerVo);
return R.ok();
}
3 service
// 2 非空判断
if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(code) ||
StringUtils.isEmpty(nickname) || StringUtils.isEmpty(password)) {
throw new GuliException(20001, "注册失败");
}
// 判断验证码
// 先获取redis中的验证码
String redisCode = redisTemplate.opsForValue().get(mobile);
if (code.equals(redisCode)) {
throw new GuliException(20001, "注册失败");
}
// 相同判断:手机号不能重复
QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
wrapper.eq("mobile", mobile);
Integer count = baseMapper.selectCount(wrapper);
if (count > 0) {
throw new GuliException(20001, "注册失败");
}
// 添加数据到数据库
UcenterMember member = new UcenterMember();
member.setMobile(mobile);
member.setNickname(nickname);
member.setPassword(MD5.encrypt(password)); // 密码加密后存储
member.setIsDisabled(false); // 用户不禁用
member.setAvatar("http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132");
baseMapper.insert(member);
}
4 swagger测试
成功
7 根据token获取用户信息
// 根据token获取用户信息
@GetMapping("getMemberInfo")
public R getMemberInfo(HttpServletRequest request) {
// 调用jwt工具类方法,根据request对象,获取头信息,返回用户id
String memberId = JwtUtils.getMemberIdByJwtToken(request);
// 查询数据库,根据用户id,获取用户信息
UcenterMember member = memberService.getById(memberId);
return R.ok().data("userInfo", member);
}
8 整合首页登录和注册
1 安装插件
npm install element-ui
npm install vue-qriously (微信支付)
2 在nuxt环境中使用element-ui插件
import Vue from 'vue'
import VueAwesomeSwiper from 'vue-awesome-swiper/dist/ssr'
import VueQriously from 'vue-qriously'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(VueAwesomeSwiper)
Vue.use(VueQriously)
Vue.use(ElementUI)
3 整合页面
- 在layouts文件夹创建登录注册页面布局sign.vue
- 修改登录和注册超链接地址 在default.vue
- 在pages创建register.vue login.vue,复制页面代码
4 整合注册前端
-
api中创建register.js,注册接口相关代码
// 根据手机号发送验证码 sendCode(phone) { return request({ url: `/edumsm/msm/send/${phone}`, methods: 'post' }) }, // 注册 registerMember(formItem) { return request({ url: `/educenter/member/register`, methods: 'post', data: formItem }) }
-
在页面中调用
js定时器方法设计倒计时:setInterval(“”, 60)
格式校验:
:rules="[{ required: true, message: '请输入手机号码', trigger: 'blur' },{validator:checkPhone, trigger: 'blur'}]
检查nginx端口是否正确,停掉并重启 nginx.exe -s stop
跨域问题