(1) 思路
(2) 获取验证码
① 调整页面 protal.html
<li><a href="reg.html" th:href="@{/auth/member/to/reg/page.html}">注册</a></li>
② CrowdWebMvcConfig
因为没有springwebmvc的配置文件,所以可以实现接口WebMvcConfigurer,然后再添加 ViewController
@Configuration
public class CrowdWebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 浏览器访问的地址
String urlPath = "/auth/member/to/reg/page.html";
// 目标视图的名称
String viewName = "member-reg";
// 添加一个viewController
registry.addViewController(urlPath).setViewName(viewName);
}
}
③ 引入 member-reg.html
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="keys" content="">
<meta name="author" content="">
<base th:href="@{/}"/>
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="css/font-awesome.min.css">
<link rel="stylesheet" href="css/login.css">
<style></style>
<script type="text/javascript" src="jquery/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="layer/layer.js"></script>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<div><a class="navbar-brand" href="index.html" style="font-size:32px;">尚筹网-创意产品众筹平台</a></div>
</div>
</div>
</nav>
<div class="container">
<form class="form-signin" role="form">
<h2 class="form-signin-heading"><i class="glyphicon glyphicon-log-in"></i> 用户注册</h2>
<div class="form-group has-success has-feedback">
<input type="text" class="form-control" id="inputSuccess1" placeholder="请输入登录账号" autofocus>
<span class="glyphicon glyphicon-user form-control-feedback"></span>
</div>
<div class="form-group has-success has-feedback">
<input type="text" class="form-control" id="inputSuccess2" placeholder="请输入登录密码" style="margin-top:10px;">
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<div class="form-group has-success has-feedback">
<input type="text" class="form-control" id="inputSuccess3" placeholder="请输入邮箱地址" style="margin-top:10px;">
<span class="glyphicon glyphicon glyphicon-envelope form-control-feedback"></span>
</div>
<div class="form-group has-success has-feedback">
<input name="phoneNum" type="text" class="form-control" id="inputSuccess4" placeholder="请输入手机号" style="margin-top:10px;">
<span class="glyphicon glyphicon glyphicon-earphone form-control-feedback"></span>
</div>
<div class="form-group has-success has-feedback">
<input type="text" class="form-control" id="inputSuccess5" placeholder="请输入验证码" style="margin-top:10px;">
<span class="glyphicon glyphicon glyphicon-comment form-control-feedback"></span>
</div>
<button type="button" id="getCodeBtn" class="btn btn-lg btn-success btn-block"> 获取验证码</button>
<a class="btn btn-lg btn-success btn-block" href="login.html" > 注册</a>
</form>
</div>
</body>
</html>
④ 为按钮 “获取验证码” 添加单击事件
<script type="text/javascript">
$(function () {
// 绑定"获取验证码"按钮的单击事件
$("#getCodeBtn").click(function () {
// 从页面获取手机号
var phoneNum = $("[name=phoneNum]").val();
// 发送 ajax 请求
$.ajax({
url: "auth/member/send/short/message.json",
type: "post",
data: {
"phoneNum": phoneNum
},
datatype: "json",
success: function (response) {
var result = response.operationResult;
if(result == "SUCCESS") {
layer.msg("发送成功!");
}
if(result == "FAILED"){
layer.msg("发送失败!请再试一次");
}
},
error :function (response) {
layer.msg(response.status + " " + response.status);
}
});
});
});
</script>
⑤ 创建 ShortMessageProperties
为了能在 yaml 中配置调用 api 的其他参数,需要编写一个带有@ConfigurationProperties注解的类。
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component
@ConfigurationProperties(prefix = "short.message")
public class ShortMessageProperties {
private String host;
private String path;
private String method;
private String appCode;
private String skin;
}
⑥ 编写 application.yml
每个人购买的api不同,各种参数也会不同
short:
message:
host:
path:
method:
appCode:
skin:
⑦ 创建 MemberHandler
用来处理刚才的 ajax 请求
@Controller
public class MemberHandler {
@Autowired
private ShortMessageProperties shortMessageProperties;
@Resource
private RedisRemoteService redisRemoteService;
@ResponseBody
@RequestMapping("/auth/member/send/short/message.json")
public ResultEntity<String> sendMessage(@RequestParam("phoneNum") String phoneNum) {
// 1. 发送验证码到phoneNum手机
ResultEntity<String> sendMessageResultEntity = CrowdUtil.sendCodeByShortMessage(
shortMessageProperties.getHost(),
shortMessageProperties.getPath(),
shortMessageProperties.getMethod(),
shortMessageProperties.getAppCode(),
phoneNum,
shortMessageProperties.getSkin()
);
// 判断信息是否发送成功
if(ResultEntity.SUCCESS.equals(sendMessageResultEntity.getOperationResult())) {
// 如果发送成功就将验证码存入redis
// 从上一部中获取随机生成的验证码
String queryData = sendMessageResultEntity.getQueryData();
// 拼接一个用在redis中存储数据的key
String key = CrowdConstant.REDIS_CODE_PREFIX + phoneNum;
// 调用远程接口存入redis
ResultEntity<String> stringResultEntity = redisRemoteService.setRedisKeyValueRemoteWithTimeout(key, queryData, 15, TimeUnit.MINUTES);
if (ResultEntity.SUCCESS.equals(stringResultEntity.getOperationResult())) {
return ResultEntity.successWithoutData();
} else {
return stringResultEntity;
}
}else {
return sendMessageResultEntity;
}
}
}
需要在主程序上添加 @EnableFeignClients,因为 redisRemoteService 接口使用了 @FeignClient(“atguigu-crowd-redis”)
⑧ 将接口封装到 工具类Util 中
/*
第三方接口,短信
@param: host 短信接口调用的URL地址
@param: path 具体发送短信功能的地址
@param: method 请求方式
@param: appCode 调用第三方短信API接口的AppCode
@param: phone 接收短信的手机号
@param: skin 模板编号
成功的话:返回验证码消息
失败的话:返回错误消息
*/
public static ResultEntity<String> sendCodeByShortMessage(
String host,
String path,
String method,
String appCode,
String phone,
String skin
) {
// 生成验证码
StringBuilder builder = new StringBuilder();
for(int i = 0; i < 4; i++){
int random = (int) (Math.random() * 10);
builder.append(random);
}
String code = builder.toString();
Map<String, String> headers = new HashMap<String, String>();
//最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
headers.put("Authorization", "APPCODE " + appCode);
// 封装其他参数
Map<String, String> querys = new HashMap<String, String>();
// 接收人手机号码
querys.put("receive", phone);
// 要发送的验证码,也就是模板中会变化的部分(短信中的变量,如:验证码)
querys.put("tag", code);
// 模板ID(联系客服开通)测试模板id:M09DD535F4
// 模板内容为:【测试签名】您好,本次验证码是:xxxx。仅供测试使用!(如需自定义模板,请在线联系我们工作人员)
querys.put("templateId", skin);
Map<String, String> bodys = new HashMap<String, String>();
try {
/**
* 重要提示如下:
* HttpUtils请从
* https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java
* 下载
*
* 相应的依赖请参照
* https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml
*/
HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);
System.out.println(response.toString());
//获取response的body
System.out.println(EntityUtils.toString(response.getEntity()));
// 取到响应状态码
int statusCode = response.getStatusLine().getStatusCode();
if(statusCode == 200) {
System.out.println("正常请求计费");
// 返回验证码
return ResultEntity.successWithData(code);
} else {
String message = "请求错误(不计费)";
// 返回错误信息
return ResultEntity.failed(message);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
至此,实现了从主页页面点击 “注册”,到达注册页面,然后点击获取验证码,对应的手机可以接收到一个随机生成的四位的验证码,同时在
redis中存入 这个接收者和验证码 的信息
(3) 执行注册
① 思路
---- mysql-provider 部分—
② MySQLRemoteService
// 保存MemberPO对象到mysql中的远程调用方法
@RequestMapping("/save/member/remote")
ResultEntity<String> saveMember(@RequestBody MemberPO memberPO);
③ MemberProviderHandler
// 保存MemberPO对象到mysql中
@RequestMapping("/save/member/remote")
ResultEntity<String> saveMember(@RequestBody MemberPO memberPO) {
try {
memberService.saveMember(memberPO);
// 执行成功
return ResultEntity.successWithoutData();
}catch (Exception e) {
// 重复异常
if (e instanceof DuplicateKeyException) {
return ResultEntity.failed(CrowdConstant.MESSAGE_LOGIN_ACCT_ALREADY_IN_USE);
}else {
return ResultEntity.failed(e.getMessage());
}
}
}
④ MemberService
void saveMember(MemberPO memberPO);
⑤ MemberServiceImpl
// 该类设置了@Transactional(readOnly = true)针对查询操作设置事务属性,所以这里要加注解,不然会报错
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
@Override
public void saveMember(MemberPO memberPO) {
memberPOMapper.insertSelective(memberPO);
}
⑥ 使用 postman 等辅助工具测试能否成功发送请求
---- 用户认证 部分—
防止 zuul 连接或者通信超时
ribbon:
ConnectTimeout: 10000 # 连接超时时间(ms)
ReadTimeout: 10000 # 通信超时时间(ms)
⑦ 调整 member-reg.html
⑧ MemberHandler
// 执行注册
@RequestMapping("/auth/do/member/register")
public String register(MemberVO memberVO, ModelMap map) {
// 1 从memberVO中取出验证码
String formCode = memberVO.getCode();
// 2 从redis中取出对应的验证码
// ① 从表单中获取手机号
String phoneNum = memberVO.getPhoneNum();
// ② 拼接redis中存储的key
String key = CrowdConstant.REDIS_CODE_PREFIX + phoneNum;
// ③ 从数据库中取出该key对应的value
ResultEntity<String> resultEntity = redisRemoteService.getRedisStringValueByKeyRemote(key);
// ④ 取出操作结果,用来判断查询操作是否有效
String result = resultEntity.getOperationResult();
// ⑥ 如果查询操作失败
if (ResultEntity.FAILED.equals(result)) {
// 存储失败信息
map.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE,resultEntity.getOperationMessage());
// 再次跳转到注册页面
return "member-reg";
}
// ⑦ 若查询结果有效,则取出redis中存储的验证码
String redisCode = resultEntity.getQueryData();
// ⑧ 判断是否取到queryData
if (redisCode == null) {
// 将错误信息存入request域中
map.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_CODE_NOT_EXISTS);
// 跳转到注册页面
return "member-reg";
}
// 3 如果成功取出验证码,比较表单中提交的验证码和redis中的验证码是否相同
// ① 如果二者不相等
if(!formCode.equals(redisCode)) {
// 将错误信息存入request域中
map.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE,CrowdConstant.MESSAGE_CODE_INVALID);
// 返回注册页面
return "member-reg";
}
// ② 如果二者相等,删除redis中保存的验证码信息,防止多次使用同一个验证码注册
ResultEntity<String> removeRedisKeyRemoteEntity = redisRemoteService.removeRedisKeyRemote(key);
log.info("redis是否删除成功: " + removeRedisKeyRemoteEntity.getOperationMessage());
// 4 注册成功,将注册信息存入mysql中
// ① 对密码进行加密
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String oldPwd = memberVO.getUserpswd();
String newPwd = bCryptPasswordEncoder.encode(oldPwd);
// ② 将密码更换为加密后的密码
memberVO.setUserpswd(newPwd);
// ③ 先将MemberVo 转换为 MemberPO
MemberPO memberPO = new MemberPO();
BeanUtils.copyProperties(memberVO,memberPO);
// ④ 执行保存,将注册信息存入mysql中
ResultEntity<String> saveMemberEntity = mySQLRemoteService.saveMember(memberPO);
// ⑤ 取出操作结果,判断是否保存成功
String saveMemberResult = saveMemberEntity.getOperationResult();
// ⑥ 如果保存失败
if (ResultEntity.FAILED.equals(saveMemberResult)) {
// 将错误信息存入request域中
map.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE,saveMemberEntity.getOperationMessage());
// 跳转到 注册页面
return "member-reg";
}
// 5 注册成功,跳转到登录页面
return "member-login";
}
创建一个登陆界面备用