结合我们前面介绍的商城的架构我们需要单独的搭建一个认证服务。
首先创建一个SpringBoot项目,然后添加对应的依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.4.12version>
<relativePath/>
parent>
<groupId>com.msb.mallgroupId>
<artifactId>mall-auth-serverartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>mall-auth_servername>
<description>认证服务description>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>2020.0.1spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>com.msb.mallgroupId>
<artifactId>mall-commonsartifactId>
<version>0.0.1-SNAPSHOTversion>
<exclusions>
<exclusion>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
exclude>
excludes>
configuration>
plugin>
plugins>
build>
project>
我们需要把认证服务注册到Nacos中,添加对应的依赖,然后完成对应的配置
# Nacos服务注册
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.56.100:8848
application:
name: mall-auth_server
# 统一的全局的--设置服务器响应给客户端的日期时间格式
jackson:
date-format: yyyy-MM-dd HH:mm:ss
thymeleaf:
cache: false # 关闭Thymeleaf的缓存
server:
port: 30000
放开Nacos注册中心
然后启动测试
然后我们整理登录和注册的相关资源,首先把登录和注册的模板文件拷贝进项目
然后把对应的静态文件拷贝到Nginx中。
然后我们需要在host文件中添加对应的配置
修改Nginx的反向代理的配置
然后修改网关服务
最后调整登录和注册页面的静态资源文件的路径.
注册服务的名称:msb-auth,启动对应的服务,测试
登录页面
注册页面
先处理验证码的页面,使其能够倒数操作
JS代码
$(function(){
$("#sendCode").click(function(){
if($(this).hasClass("d1")){
// 说明正在倒计时
}else{
// 给指定的手机号发送验证码
timeoutChangeStyle()
}
});
})
var num = 10
function timeoutChangeStyle(){
$("#sendCode").attr("class","d1")
if(num == 0){
// 说明1分钟到了,可以再次发送验证码了
$("#sendCode").text("发送验证码")
num= 10;
$("#sendCode").attr("class","")
}else{
setTimeout('timeoutChangeStyle()',1000)
$("#sendCode").text(num+"s后再次发送")
}
num --;
}
通过阿里云的短信服务来实现。我们直接购买0元15次就可以了。https://www.aliyun.com/
进入到对应的管理控制台,查看对应的信息
通过短信供应商提供的相关的编程语言的开发模板开发即可
供应商提供了对应的HttpUtils工具类,我们需要下载保存到我们自己的项目中。
然后封装对应的发送验证码的接口
package com.msb.mall.third.utils;
import lombok.Data;
import org.apache.http.HttpResponse;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* 短信组件
*/
@ConfigurationProperties(prefix = "spring.cloud.alicloud.sms")
@Data
@Component
public class SmsComponent {
private String host;
private String path;
private String method = "POST";
private String appCode;
/**
* 发送短信验证码
* @param phone 发送的手机号
* @param code 发送的短信验证码
*/
public void sendSmsCode(String phone,String code){
Map<String, String> headers = new HashMap<String, String>();
//最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
headers.put("Authorization", "APPCODE " + appCode);
//根据API的要求,定义相对应的Content-Type
headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
Map<String, String> querys = new HashMap<String, String>();
Map<String, String> bodys = new HashMap<String, String>();
bodys.put("content", "code:"+code);
bodys.put("phone_number", phone);
bodys.put("template_id", "TPL_0000");
try {
HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);
System.out.println(response.toString());
//获取response的body
//System.out.println(EntityUtils.toString(response.getEntity()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
添加对应的属性信息
然后我们将短信功能串联起来
我们需要在第三方服务中提供对外的接口服务
@RestController
public class SMSController {
@Autowired
private SmsComponent smsComponent;
/**
* 调用短信服务商提供的短信API发送短信
* @param phone
* @param code
* @return
*/
@GetMapping("/sms/sendcode")
public R sendSmsCode(@RequestParam("phone") String phone,@RequestParam("code") String code){
smsComponent.sendSmsCode(phone,code);
return R.ok();
}
}
我们需要在认证服务中通过feign来调用third中提供的短信服务,同时给客户端提供访问的接口
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-loadbalancerartifactId>
dependency>
放开注解
然后声明Feign的接口
定义对应的Controller
@Controller
public class LoginController {
@Autowired
private ThirdPartFeginService thirdPartFeginService;
@ResponseBody
@GetMapping("/sms/sendCode")
public R sendSmsCode(@RequestParam("phone") String phone){
// 生成随机的验证码
String code = UUID.randomUUID().toString().substring(0, 5);
thirdPartFeginService.sendSmsCode(phone,code);
return R.ok();
}
}
然后我们需要在页面中通过jQuery的异步提交来发送短信
当我们把验证码通过短信的形式发送给你客户手机,然后我们需要把手机号和对应的验证码存储起来,后面可能会集群部署,这时我们把这个信息存在在Redis中。
添加配置
存储数据
搞定
针对验证码发送的间隔必须是60秒以上,这时我们可以在保存到Redis中的数据的值我们加上发送的时间来处理
模板页面也需要做出对应的处理
表单提交的注册数据我们通过JSR303来验证。
首先定义VO对象
package com.msb.mall.vo;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
/**
* 注册用户的VO对象
*/
@Data
public class UserRegisterVo {
@NotEmpty(message = "账号不能为空")
@Length(min = 3,max = 15,message = "账号必须是3~15位")
private String userName; // 账号
@NotEmpty(message = "密码不能为空")
@Length(min = 3,max = 15,message = "密码必须是3~15位")
private String password; // 密码
@NotEmpty(message = "手机号不能为空")
@Pattern(regexp = "^[1][3-9][0-9]{9}$",message = "手机号不合法")
private String phone; // 手机号
@NotEmpty(message = "验证码不能为空")
private String code; // 验证码
}
然后就是控制器的逻辑代码
@PostMapping("/sms/register")
public String register(@Valid UserRegisterVo vo, BindingResult result, Model model){
if(result.hasErrors()){
// 表示提交的数据不合法
List<FieldError> fieldErrors = result.getFieldErrors();
Map<String,String> map = new HashMap<>();
for (FieldError fieldError : fieldErrors) {
String field = fieldError.getField();
String defaultMessage = fieldError.getDefaultMessage();
map.put(field,defaultMessage);
}
model.addAttribute("error",map);
return "/reg";
}
// 表单提交的注册的数据是合法的
return "redirect:/login.html";
}
然后就是页面代码处理
验证码的校验
会员服务处理
控制器
/**
* 会员注册
* @return
*/
@PostMapping("/register")
public R register(@RequestBody MemberReigerVO vo){
try {
memberService.register(vo);
}catch (UsernameExsitException exception){
return R.error(BizCodeEnume.USERNAME_EXSIT_EXCEPTION.getCode(),
BizCodeEnume.USERNAME_EXSIT_EXCEPTION.getMsg());
}catch (PhoneExsitExecption exsitExecption) {
return R.error(BizCodeEnume.PHONE_EXSIT_EXCEPTION.getCode(),
BizCodeEnume.PHONE_EXSIT_EXCEPTION.getMsg());
}catch (Exception e){
return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),
BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
}
return R.ok();
}
然后对应的Service
/**
* 完成会员的注册功能
* @param vo
*/
@Override
public void register(MemberReigerVO vo) throws PhoneExsitExecption,UsernameExsitException{
MemberEntity entity = new MemberEntity();
// 设置会员等级 默认值
MemberLevelEntity memberLevelEntity = memberLevelService.queryMemberLevelDefault();
entity.setLevelId(memberLevelEntity.getId()); // 设置默认的会员等级
// 添加对应的账号和手机号是不能重复的
checkUsernameUnique(vo.getUserName());
checkPhoneUnique(vo.getPhone());
entity.setUsername(vo.getUserName());
entity.setMobile(vo.getPhone());
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String encode = encoder.encode(vo.getPassword());
// 需要对密码做加密处理
entity.setPassword(encode);
// 设置其他的默认值
this.save(entity);
}
auth服务通过Fegin远程调用
远程调用
@PostMapping("/sms/register")
public String register(@Valid UserRegisterVo vo, BindingResult result, Model model){
Map<String,String> map = new HashMap<>();
if(result.hasErrors()){
// 表示提交的数据不合法
List<FieldError> fieldErrors = result.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
String field = fieldError.getField();
String defaultMessage = fieldError.getDefaultMessage();
map.put(field,defaultMessage);
}
model.addAttribute("error",map);
return "/reg";
}else{
// 验证码是否正确
String code = (String)redisTemplate.opsForValue().get(SMSConstant.SMS_CODE_PERFIX + vo.getPhone());
code = code.split("_")[0];
if(!code.equals(vo.getCode())){
// 说明验证码不正确
map.put("code","验证码错误");
model.addAttribute("error",map);
return "/reg";
}else{
// 验证码正确 删除验证码
redisTemplate.delete(SMSConstant.SMS_CODE_PERFIX + vo.getPhone());
// 远程调用对应的服务 完成注册功能
R r = memberFeginService.register(vo);
if(r.getCode() == 0){
// 注册成功
return "redirect:http://msb.auth.com/login.html";
}else{
// 注册失败
map.put("msg",r.getCode()+":"+r.get("msg"));
model.addAttribute("error",map);
return "/reg";
}
}
}
//return "redirect:/login.html";
}
完成对应的服务注册
校验提示