由于尚硅谷的视频是通过Eclipse软件来做的,其中有些操作与IDEA上有所区别,所以我在这里将IDEA区别于Eclipse的操作、操作过程中涉及的源码(与视频的源码略有出入)以及大家可能遇到的种种问题分享给大家,这些源码在我这里均是通过测试的,仅供参考!
1 短信服务
1.1 短信服务测试
这部分需要自己去阿里云购买短信服务,每个人的api接口可能都不同,这里只上传我购买api的相关代码,把需要修改的几个地方标注出来的,具体操作请看视频!
修改authentication-consumer\pom.xml
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<scope>test</scope>
</dependency>
新建authentication-consumer\src\test\java\com\aiguigu\crowd\test\CrowdTest.java
package com.aiguigu.crowd.test;
import com.aliyun.api.gateway.demo.util.HttpUtils;
import org.apache.http.HttpResponse;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.HashMap;
import java.util.Map;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = CrowdTest.class)
public class CrowdTest {
@Test
public void testSendMessage() {
String host = "https://cxkjsms.market.alicloudapi.com";
String path = "/chuangxinsms/dxjk";
String method = "POST";
String appcode = "这部分需要自己填写";
//开通服务后 买家中心-查看AppCode
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("content", "【创信】你的验证码是:5873,3分钟内有效!");
querys.put("mobile", "填写自己手机号");
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()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
新建authentication-consumer\src\main\java\com\aliyun\api\gateway\demo\util\HttpUtils.java
package com.aliyun.api.gateway.demo.util;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
public class HttpUtils {
/**
* get
*
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @return
* @throws Exception
*/
public static HttpResponse doGet(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpGet request = new HttpGet(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
return httpClient.execute(request);
}
/**
* post form
*
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @param bodys
* @return
* @throws Exception
*/
public static HttpResponse doPost(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys,
Map<String, String> bodys)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPost request = new HttpPost(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (bodys != null) {
List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>();
for (String key : bodys.keySet()) {
nameValuePairList.add(new BasicNameValuePair(key, bodys.get(key)));
}
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, "utf-8");
formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
request.setEntity(formEntity);
}
return httpClient.execute(request);
}
/**
* Post String
*
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @param body
* @return
* @throws Exception
*/
public static HttpResponse doPost(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys,
String body)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPost request = new HttpPost(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (StringUtils.isNotBlank(body)) {
request.setEntity(new StringEntity(body, "utf-8"));
}
return httpClient.execute(request);
}
/**
* Post stream
*
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @param body
* @return
* @throws Exception
*/
public static HttpResponse doPost(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys,
byte[] body)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPost request = new HttpPost(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (body != null) {
request.setEntity(new ByteArrayEntity(body));
}
return httpClient.execute(request);
}
/**
* Put String
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @param body
* @return
* @throws Exception
*/
public static HttpResponse doPut(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys,
String body)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPut request = new HttpPut(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (StringUtils.isNotBlank(body)) {
request.setEntity(new StringEntity(body, "utf-8"));
}
return httpClient.execute(request);
}
/**
* Put stream
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @param body
* @return
* @throws Exception
*/
public static HttpResponse doPut(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys,
byte[] body)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpPut request = new HttpPut(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
if (body != null) {
request.setEntity(new ByteArrayEntity(body));
}
return httpClient.execute(request);
}
/**
* Delete
*
* @param host
* @param path
* @param method
* @param headers
* @param querys
* @return
* @throws Exception
*/
public static HttpResponse doDelete(String host, String path, String method,
Map<String, String> headers,
Map<String, String> querys)
throws Exception {
HttpClient httpClient = wrapClient(host);
HttpDelete request = new HttpDelete(buildUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
request.addHeader(e.getKey(), e.getValue());
}
return httpClient.execute(request);
}
private static String buildUrl(String host, String path, Map<String, String> querys) throws UnsupportedEncodingException {
StringBuilder sbUrl = new StringBuilder();
sbUrl.append(host);
if (!StringUtils.isBlank(path)) {
sbUrl.append(path);
}
if (null != querys) {
StringBuilder sbQuery = new StringBuilder();
for (Map.Entry<String, String> query : querys.entrySet()) {
if (0 < sbQuery.length()) {
sbQuery.append("&");
}
if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
sbQuery.append(query.getValue());
}
if (!StringUtils.isBlank(query.getKey())) {
sbQuery.append(query.getKey());
if (!StringUtils.isBlank(query.getValue())) {
sbQuery.append("=");
sbQuery.append(URLEncoder.encode(query.getValue(), "utf-8"));
}
}
}
if (0 < sbQuery.length()) {
sbUrl.append("?").append(sbQuery);
}
}
return sbUrl.toString();
}
private static HttpClient wrapClient(String host) {
HttpClient httpClient = new DefaultHttpClient();
if (host.startsWith("https://")) {
sslClient(httpClient);
}
return httpClient;
}
private static void sslClient(HttpClient httpClient) {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] xcs, String str) {
}
public void checkServerTrusted(X509Certificate[] xcs, String str) {
}
};
ctx.init(null, new TrustManager[] { tm }, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx);
ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager ccm = httpClient.getConnectionManager();
SchemeRegistry registry = ccm.getSchemeRegistry();
registry.register(new Scheme("https", 443, ssf));
} catch (KeyManagementException ex) {
throw new RuntimeException(ex);
} catch (NoSuchAlgorithmException ex) {
throw new RuntimeException(ex);
}
}
}
测试:运行testSendMessage(),可收到短信:
1.2 加入项目
修改util\src\main\java\com\atguigu\crowd\util\CrowdUtil.java
public static ResultEntity<String> sendShortMessage(String phoneNumbers) {
String host = "https://cxkjsms.market.alicloudapi.com";
String path = "/chuangxinsms/dxjk";
String method = "POST";
String appcode = "5ed7aeac19cf4c429048a1af7b603055";//开通服务后 买家中心-查看AppCode
Map<String, String> headers = new HashMap<String, String>();
// 生成验证码
StringBuilder builder = new StringBuilder();
for(int i = 0; i < 4; i++) {
int random = (int) (Math.random() * 10);
builder.append(random);
}
String code = builder.toString();
//最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
headers.put("Authorization", "APPCODE " + appcode);
Map<String, String> querys = new HashMap<String, String>();
querys.put("content", "【创信】你的验证码是:"+ code +",3分钟内有效!");
querys.put("mobile", phoneNumbers);
Map<String, String> bodys = new HashMap<String, String>();
try {
HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
String reasonPhrase = statusLine.getReasonPhrase();
if(statusCode == 200){
return ResultEntity.successWithData(code);
}
return ResultEntity.failed(reasonPhrase);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
2 前往注册界面
2.1 创建view-controller
新建authentication-consumer\src\main\java\com\atguigu\crowd\config\CrowdWebMvcConfig.java
package com.atguigu.crowd.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CrowdWebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 浏览器访问的地址
String urlPath = "/auth/member/to/reg/page.html";
// 目标视图的名称,将来拼接前后缀
String viewName = "member-reg";
// 添加view-controller
registry.addViewController(urlPath).setViewName(viewName);
}
}
2.2 新建注册页面
修改authentication-consumer\src\main\resources\templates\portal.html
<li><a href="reg.html" th:href="@{/auth/member/to/reg/page.html}">注册</a></li>
新建authentication-consumer\src\main\resources\templates\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.css">
<link rel="stylesheet" href="css/font-awesome.min.css">
<link rel="stylesheet" href="css/login.css">
<script src="jquery/jquery-3.6.0.js"></script>
<script src="bootstrap/js/bootstrap.js"></script>
<script type="text/javascript" src="layer/layer.js"></script>
<script type="text/javascript">
layer.msg("aaaa...");
</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="inputSuccess4" 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="inputSuccess4" 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="inputSuccess4" 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 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="inputSuccess4" placeholder="请输入验证码" style="margin-top:10px;">
<span class="glyphicon glyphicon glyphicon-comment form-control-feedback"></span>
</div>
<button 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>
2.3 发送验证码
2.3.1 前端部分
修改authentication-consumer\src\main\resources\templates\member-reg.html
<button id="sendBtn" type="button" class="btn btn-lg btn-success btn-block"> 获取验证码</button>
<input type="text" class="form-control" name="phoneNum" id="inputSuccess4" placeholder="请输入手机号" style="margin-top:10px;">
<script type="text/javascript">
$(function (){
$("#sendBtn").click(function (){
// 1.获取接收短信的手机号
var phoneNum = $.trim($("[name=phoneNum]").val());
// 2.发送请求
$.ajax({
url: "auth/member/send/short/message.json",
type: "post",
data: {
phoneNum: phoneNum
},
dataType: "json",
success: function (response){
var result = response.result;
if (result == "SUCCESS") {
layer.msg("发送成功");
}
if (result == "FAILED") {
layer.msg("发送失败!请再试一次!")
}
},
error: function (response){
layer.msg(response.status + " " + response.statusText);
}
});
});
});
</script>
2.3.2 后端部分
修改util\src\main\java\com\atguigu\crowd\constant\CrowdConstant.java
public static final String REDIS_CODE_PREFIX = "REDIS_CODE_PREFIX_";
新建consumer\src\main\java\com\atguigu\crowd\handler\MemberHandler.java
package com.atguigu.crowd.handler;
import com.atguigu.crowd.api.RedisRemoteService;
import com.atguigu.crowd.config.ShortMessageProperties;
import com.atguigu.crowd.constant.CrowdConstant;
import com.atguigu.crowd.util.CrowdUtil;
import com.atguigu.crowd.util.ResultEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.concurrent.TimeUnit;
@Controller
public class MemberHandler {
@Autowired
private ShortMessageProperties shortMessageProperties;
@Autowired
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.sendShortMessage(
shortMessageProperties.getHost(),
shortMessageProperties.getPath(),
shortMessageProperties.getMethod(), phoneNum,
shortMessageProperties.getAppCode());
// 2.判断是否发送成功
if (ResultEntity.SUCCESS.equals(sendMessageResultEntity.getResult())) {
// 3.如果成功,则将验证码存入Redis
String code = sendMessageResultEntity.getData();
String key = CrowdConstant.REDIS_CODE_PREFIX + phoneNum;
ResultEntity<String> saveCodeResultEntity = redisRemoteService.setRedisKeyValueRemoteWithTimeout(key, code, 15, TimeUnit.MINUTES);
// 4. 判断是否成功存入 Redis
if (ResultEntity.SUCCESS.equals(saveCodeResultEntity.getResult())) {
return ResultEntity.successWithoutData();
}else {
return saveCodeResultEntity;
}
}else {
return sendMessageResultEntity;
}
}
}
新建authentication-consumer\src\main\java\com\atguigu\crowd\config\ShortMessageProperties.java
package com.atguigu.crowd.config;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@NoArgsConstructor
@AllArgsConstructor
@Data
@Component
@ConfigurationProperties(prefix = "short.message")
public class ShortMessageProperties {
private String host;
private String path;
private String method;
private String appCode;
}
修改authentication-consumer\src\main\resources\application.yml
short:
message:
app-code: 5ed7aeac19cf4c429048a1af7b603055
host: https://cxkjsms.market.alicloudapi.com
method: POST
path: /chuangxinsms/dxjk
修改authentication-consumer\src\main\java\com\atguigu\crowd\CrowdMainClass.java
@EnableFeignClients
2.3.3 测试
可以正常收到短信,并将映射记录在redis中:
注:1和2由于每个人所购买的api不同,所以代码部分也不尽相同,这部分仅供思路参考,尽量不要随意CV!
另:测试时可能会包ResultEntity类构造器缺失,可通过将lombok类import至util\src\main\java\com\atguigu\crowd\util\ResultEntity.java来解决:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
3 用户注册
3.1 SQL存储
在SQLyog给loginacct增加唯一约束:
ALTER TABLE `project_crowd`.`t_member` ADD UNIQUE INDEX (`loginacct`);
修改mysql-provider\src\main\java\com\atguigu\crowd\handler\MemberProviderHandler.java
@RequestMapping("/save/member/remote")
public 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);
return ResultEntity.failed(e.getMessage());
}
}
并新增saveMember()方法及实现类:
修改mysql-provider\src\main\java\com\atguigu\crowd\service\impl\MemberServiceImpl.java
@Transactional(
propagation = Propagation.REQUIRES_NEW,
rollbackFor = Exception.class,
readOnly = false)
@Override
public void saveMember(MemberPO memberPO) {
memberPOMapper.insertSelective(memberPO);
}
修改api\src\main\java\com\atguigu\crowd\api\MySQLRemoteService.java
@RequestMapping("/save/member/remote")
public ResultEntity<String> saveMember(@RequestBody MemberPO memberPO);
3.2 测试
使用Postman,设置POST地址:
http://localhost:2000/save/member/remote
请求头:
Content-Type:application/json;charset=UTF-8
请求体:
{
"loginacct":"Heygo",
"userpswd":123123
}
再次提交:
查看SQLyog:
3.3 auth-consumer
修改authentication-consumer\src\main\resources\templates\member-reg.html
<div class="container">
<form action="/auth/do/member/register" method="post" class="form-signin" role="form">
<h2 class="form-signin-heading"><i class="glyphicon glyphicon-log-in"></i> 用户注册</h2>
<p th:text="${message}">这里显示从请求域取出的提示消息</p>
<div class="form-group has-success has-feedback">
<input type="text" name="loginacct" class="form-control" id="inputSuccess4" placeholder="请输入登录账号" autofocus>
<span class="glyphicon glyphicon-user form-control-feedback"></span>
</div>
<div class="form-group has-success has-feedback">
<input type="text" name="userpswd" class="form-control" id="inputSuccess4" 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" name="username" class="form-control" id="inputSuccess4" 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" name="email" class="form-control" id="inputSuccess4" 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 type="text" name="phoneNum" 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" name="code" class="form-control" id="inputSuccess4" placeholder="请输入验证码" style="margin-top: 10px;">
<span class="glyphicon glyphicon glyphicon-comment form-control-feedback"></span>
</div>
<button id="sendBtn" type="button" class="btn btn-lg btn-success btn-block"> 获取验证码</button>
<button type="submit" class="btn btn-lg btn-success btn-block">注册</button>
</form>
</div>
新建authentication-consumer\src\main\java\com\atguigu\crowd\handler\MemberHandler.java
@Autowired
private MySQLRemoteService mySQLRemoteService;
@RequestMapping("/auth/do/member/register")
public String register(MemberVO memberVO, ModelMap modelMap) {
// 1.获取用户输入的手机号
String phoneNum = memberVO.getPhoneNum();
// 2.拼接Redis中存储的Key
String key = CrowdConstant.REDIS_CODE_PREFIX + phoneNum;
// 3.从Redis读取Key对应的value
ResultEntity<String> resultEntity = redisRemoteService.getRedisStringValueByKeyRemote(key);
// 4.检查查询操作是否有效
String result = resultEntity.getResult();
if (ResultEntity.FAILED.equals(result)) {
modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, resultEntity.getMessage());
return "member-reg";
}
String redisCode = resultEntity.getData();
if (redisCode == null) {
modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_CODE_NOT_EXISTS);
return "member-reg";
}
// 5.能从Redis查询到,则比较两者是否一致
String formCode = memberVO.getCode();
if (!formCode.equals(redisCode)) {
modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_CODE_INVALID);
return "member-reg";
}
// 6.一致,将value从Redis中删除
ResultEntity<String> removeResultEntity = redisRemoteService.removeRedisKeyRemote(key);
// 7.执行密码加密
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String userpswdBeforeEncode = memberVO.getUserpswd();
String userpswdAfterEncode = passwordEncoder.encode(userpswdBeforeEncode);
memberVO.setUserpswd(userpswdAfterEncode);
// 8. 执行保存
MemberPO memberPO = new MemberPO();
BeanUtils.copyProperties(memberVO, memberPO);
ResultEntity<String> saveMemberResultEntity = mySQLRemoteService.saveMember(memberPO);
if (ResultEntity.FAILED.equals(saveMemberResultEntity)) {
modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, saveMemberResultEntity.getMessage());
return "member-reg";
}
return "redirect:/auth/member/to/login/page";
}
修改util\src\main\java\com\atguigu\crowd\constant\CrowdConstant.java
public static final String MESSAGE_CODE_NOT_EXISTS = "验证码已过期!请检查手机号是否正确或重新发送!";
public static final String MESSAGE_CODE_INVALID = "验证码不正确!";
修改authentication-consumer\src\main\resources\application.yml
ribbon:
ConnectTimeout: 10000 # 连接超时时间(ms)
ReadTimeout: 10000 # 通信超时时间(ms)
修改authentication-consumer\src\main\java\com\atguigu\crowd\config\CrowdWebMvcConfig.java
registry.addViewController("/auth/member/to/login/page").setViewName("member-login");
新建authentication-consumer\src\main\resources\templates\member-login.html
不用写东西,先完成下列测试
3.4 测试
输入正确验证码后,进入创建的login界面:
注册后可在SQLyog中查询到数据:
如果输入错误手机号:
如果输入错误验证码:
如果输入同样的登录账号:
4 用户登录
4.1 前端部分
修改authentication-consumer\src\main\resources\templates\member-login.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.css">
<link rel="stylesheet" href="css/font-awesome.min.css">
<link rel="stylesheet" href="css/login.css">
<script src="jquery/jquery-3.6.0.js"></script>
<script src="bootstrap/js/bootstrap.js"></script>
<style>
</style>
</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 action="/auth/member/do/login" method="post" class="form-signin" role="form">
<h2 class="form-signin-heading">
<i class="glyphicon glyphicon-log-in"></i> 用户登录
</h2>
<p th:text="${message}">在这里显示登录失败的提示消息</p>
<div class="form-group has-success has-feedback">
<input type="text" name="loginacct" class="form-control" id="loginacct" placeholder="请输入登录账号" autofocus>
<span class="glyphicon glyphicon-user form-control-feedback"></span>
</div>
<div class="form-group has-success has-feedback">
<input type="text" name="userpswd" class="form-control" id="userpswd" placeholder="请输入登录密码" style="margin-top:10px;">
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<div class="checkbox" style="text-align:right;">
<a href="reg.html" th:href="@{/auth/member/to/login/page/}">我要注册</a>
</div>
<button type="submit" class="btn btn-lg btn-success btn-block"> 登录</button>
</form>
</div>
</body>
</html>
修改authentication-consumer\src\main\resources\templates\portal.html
<li><a href="login.html" th:href="@{/auth/member/to/login/page}">登录</a></li>
4.2 后端部分
新建entity\src\main\java\com\atguigu\crowd\entity\vo\MemberLoginVO.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MemberLoginVO {
private Integer id;
private String username;
private String email;
}
修改authentication-consumer\src\main\java\com\atguigu\crowd\handler\MemberHandler.java
@RequestMapping("/auth/member/do/login")
public String doLogin(@RequestParam("loginacct") String logincacct,
@RequestParam("userpswd") String userpswd,
ModelMap modelMap,
HttpSession session) {
// 1.调用远程接口查询登录账号
ResultEntity<MemberPO> resultEntity = mySQLRemoteService.getMemberPOByLoginAcctRemote(logincacct);
// 2.失败则返回登录页面
if (ResultEntity.FAILED.equals(resultEntity.getResult())) {
modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, resultEntity.getMessage());
return "member-login";
}
MemberPO memberPO = resultEntity.getData();
if (memberPO == null) {
modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_LOGIN_FAILED);
return "member-login";
}
// 3.比较密码
String userpswdDataBase = memberPO.getUserpswd();
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
boolean matches = passwordEncoder.matches(userpswd, userpswdDataBase);
if (!matches) {
modelMap.addAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_LOGIN_FAILED);
return "member-login";
}
// 4.创建MemberLoginVO对象存入Session域
MemberLoginVO memberLoginVO = new MemberLoginVO(memberPO.getId(), memberPO.getUsername(), memberPO.getEmail());
session.setAttribute(CrowdConstant.ATTR_NAME_LOGIN_MEMBER, memberLoginVO);
return "redirect:/auth/member/to/center/page";
}
修改util\src\main\java\com\atguigu\crowd\constant\CrowdConstant.java
public static final String ATTR_NAME_LOGIN_MEMBER = "loginMember";
新建authentication-consumer\src\main\resources\templates\member-center.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="author" content="">
<base th:href="@{/}"/>
<link rel="stylesheet" href="bootstrap/css/bootstrap.css">
<link rel="stylesheet" href="css/font-awesome.min.css">
<link rel="stylesheet" href="css/theme.css">
<script src="jquery/jquery-3.6.0.js"></script>
<script src="bootstrap/js/bootstrap.js"></script>
<script src="script/docs.min.js"></script>
<script src="script/back-to-top.js"></script>
<script src="script/echarts.js"></script>
<style>
#footer {
padding: 15px 0;
background: #fff;
border-top: 1px solid #ddd;
text-align: center;
}
#topcontrol {
color: #fff;
z-index: 99;
width: 30px;
height: 30px;
font-size: 20px;
background: #222;
position: relative;
right: 14px !important;
bottom: 11px !important;
border-radius: 3px !important;
}
#topcontrol:after {
/*top: -2px;*/
left: 8.5px;
content: "\f106";
position: absolute;
text-align: center;
font-family: FontAwesome;
}
#topcontrol:hover {
color: #fff;
background: #18ba9b;
-webkit-transition: all 0.3s ease-in-out;
-moz-transition: all 0.3s ease-in-out;
-o-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
</style>
</head>
<body>
<div class="navbar-wrapper">
<div class="container">
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="index.html" style="font-size:32px;">尚筹网-创意产品众筹平台</a>
</div>
<div id="navbar" class="navbar-collapse collapse" style="float:right;">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="glyphicon glyphicon-user"></i> [[${session.loginMember.username}]]<span class="caret"></span></a>
<li><a href="member.html"><i class="glyphicon glyphicon-scale"></i> 会员中心</a></li>
<li><a href="#"><i class="glyphicon glyphicon-comment"></i> 消息</a></li>
<li class="divider"></li>
<li><a href="index.html"><i class="glyphicon glyphicon-off"></i> 退出系统</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
</div>
</div>
<div class="container">
<div class="row clearfix">
<div class="col-sm-3 col-md-3 column">
<div class="row">
<div class="col-md-12">
<div class="thumbnail" style=" border-radius: 0px;">
<img src="img/services-box1.jpg" class="img-thumbnail" alt="">
<div class="caption" style="text-align:center;">
<h3>
[[${session.loginMember.username}]]
</h3>
<span class="label label-danger" style="cursor:pointer;" onclick="window.location.href='accttype.html'">未实名认证</span>
</div>
</div>
</div>
</div>
<div class="list-group">
<div class="list-group-item active">
资产总览<span class="badge"><i class="glyphicon glyphicon-chevron-right"></i></span>
</div>
<div class="list-group-item " style="cursor:pointer;" onclick="window.location.href='minecrowdfunding.html'">
我的众筹<span class="badge"><i class="glyphicon glyphicon-chevron-right"></i></span>
</div>
</div>
</div>
<div class="col-sm-9 col-md-9 column">
<blockquote style="border-left: 5px solid #f60;color:#f60;padding: 0 0 0 20px;">
<b>
我的钱包
</b>
</blockquote>
<div id="main" style="width: 600px;height:400px;"></div>
<blockquote style="border-left: 5px solid #f60;color:#f60;padding: 0 0 0 20px;">
<b>
理财
</b>
</blockquote>
<div id="main1" style="width: 600px;height:400px;"></div>
<blockquote style="border-left: 5px solid #f60;color:#f60;padding: 0 0 0 20px;">
<b>
比例
</b>
</blockquote>
<div id="main2" style="width: 600px;height:400px;"></div>
</div>
</div>
</div>
<div class="container" style="margin-top:20px;">
<div class="row clearfix">
<div class="col-md-12 column">
<div id="footer">
<div class="footerNav">
<a rel="nofollow" href="http://www.atguigu.com">关于我们</a> | <a rel="nofollow" href="http://www.atguigu.com">服务条款</a> | <a rel="nofollow" href="http://www.atguigu.com">免责声明</a> | <a rel="nofollow" href="http://www.atguigu.com">网站地图</a> | <a rel="nofollow" href="http://www.atguigu.com">联系我们</a>
</div>
<div class="copyRight">
Copyright ?2017-2017atguigu.com 版权所有
</div>
</div>
</div>
</div>
</div>
<script>
$('#myTab a').click(function (e) {
e.preventDefault()
$(this).tab('show')
})
$('#myTab1 a').click(function (e) {
e.preventDefault()
$(this).tab('show')
})
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
option = {
title: {
text: '七日年化收益率(%)'
},
tooltip: {
trigger: 'axis'
},
legend: {
data:['基金','股票']
},
toolbox: {
show: false,
feature: {
dataZoom: {
yAxisIndex: 'none'
},
dataView: {readOnly: false},
magicType: {type: ['line', 'bar']},
restore: {},
saveAsImage: {}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['2017-05-16','2017-05-17','2017-05-18','2017-05-19','2017-05-20','2017-05-21','2017-05-22']
},
yAxis: {
type: 'value',
axisLabel: {
formatter: '{value} '
}
},
series: [
{
name:'基金',
type:'line',
data:[1, 1, 5, 3, 2, 3, 2],
markPoint: {
data: [
{type: 'max', name: '最大值'},
{type: 'min', name: '最小值'}
]
},
markLine: {
data: [
{type: 'average', name: '平均值'}
]
}
},
{
name:'股票',
type:'line',
data:[1, -2, 2, 5, 3, 2, 4],
markPoint: {
data: [
{name: '周最低', value: -2, xAxis: 1, yAxis: -1.5}
]
},
markLine: {
data: [
{type: 'average', name: '平均值'},
[{
symbol: 'none',
x: '90%',
yAxis: 'max'
}, {
symbol: 'circle',
label: {
normal: {
position: 'start',
formatter: '最大值'
}
},
type: 'max',
name: '最高点'
}]
]
}
}
]
};
myChart.setOption(option);
var myChart1 = echarts.init(document.getElementById('main1'));
// 指定图表的配置项和数据
option1 = {
color: ['#3398DB'],
tooltip : {
trigger: 'axis',
axisPointer : { // 坐标轴指示器,坐标轴触发有效
type : 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis : [
{
type : 'category',
data : ['基金', '票据', '定期理财', '变现贷'],
axisTick: {
alignWithLabel: true
}
}
],
yAxis : [
{
type : 'value'
}
],
series : [
{
name:'直接访问',
type:'bar',
barWidth: '60%',
data:[10, 52, 200, 334, 390, 330, 220]
}
]
};
// 使用刚指定的配置项和数据显示图表。
myChart1.setOption(option1);
var myChart2 = echarts.init(document.getElementById('main2'));
// 指定图表的配置项和数据
option2 = {
title : {
text: '某站点用户访问来源',
subtext: '纯属虚构',
x:'center'
},
tooltip : {
trigger: 'item',
formatter: "{a} <br/>{b} : {c} ({d}%)"
},
legend: {
orient: 'vertical',
left: 'left',
data: ['直接访问','邮件营销','联盟广告','视频广告','搜索引擎']
},
series : [
{
name: '访问来源',
type: 'pie',
radius : '55%',
center: ['50%', '60%'],
data:[
{value:335, name:'直接访问'},
{value:310, name:'邮件营销'},
{value:234, name:'联盟广告'},
{value:135, name:'视频广告'},
{value:1548, name:'搜索引擎'}
],
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
// 使用刚指定的配置项和数据显示图表。
myChart2.setOption(option2);
</script>
</body>
</html>
修改authentication-consumer\src\main\java\com\atguigu\crowd\config\CrowdWebMvcConfig.java
registry.addViewController("/auth/member/to/center/page").setViewName("member-center");
修改mysql-provider\src\main\java\com\atguigu\crowd\service\impl\MemberServiceImpl.java
// 5.获取结果
if(list == null || list.size() == 0){
return null;
}
return list.get(0);
4.3 测试
如果输入错误的账号或者密码,则提示:
如果输入正确,则进入:
4.4 退出登录
修改authentication-consumer\src\main\resources\templates\member-center.html
<li><a href="index.html" th:href="@{/auth/member/logout}"><i class="glyphicon glyphicon-off"></i> 退出系统</a></li>
修改authentication-consumer\src\main\java\com\atguigu\crowd\handler\MemberHandler.java
@RequestMapping("/auth/member/logout")
public String logout(HttpSession session) {
session.invalidate();
return "redirect:/";
}
测试:在点击退出系统后可以退回界面
5 登录状态检查
5.1 Pom配置
修改authentication-consumer\pom.xml以及zuul\pom.xml
<!-- 引入springboot&redis整合场景 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 引入springboot&springsession整合场景 -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
5.2 Application
修改zuul\src\main\resources\application.yml
spring:
application:
name: atguigu-crowd-zuul
redis:
host: 192.168.201.100
session:
store-type: redis
修改zuul\src\main\resources\application.yml
spring:
application:
name: atguigu-crowd-auth
thymeleaf:
prefix: classpath:/templates/
suffix: .html
redis:
host: 127.0.0.1
session:
store-type: redis
5.3 放行静态资源
新建util\src\main\java\com\atguigu\crowd\constant\AccessPassResources.java
package com.atguigu.crowd.constant;
import java.util.HashSet;
import java.util.Set;
public class AccessPassResources {
public static final Set<String> PASS_RES_SET = new HashSet<>();
static {
PASS_RES_SET.add("/");
PASS_RES_SET.add("/auth/member/to/reg/page");
PASS_RES_SET.add("/auth/member/to/login/page");
PASS_RES_SET.add("/auth/member/logout");
PASS_RES_SET.add("/auth/member/do/login");
PASS_RES_SET.add("/auth/do/member/register");
PASS_RES_SET.add("/auth/member/send/short/message.json");
}
public static final Set<String> STATIC_RES_SET = new HashSet<>();
static {
STATIC_RES_SET.add("bootstrap");
STATIC_RES_SET.add("css");
STATIC_RES_SET.add("fonts");
STATIC_RES_SET.add("img");
STATIC_RES_SET.add("jquery");
STATIC_RES_SET.add("layer");
STATIC_RES_SET.add("script");
STATIC_RES_SET.add("ztree");
}
/**
* 用于判断某个 ServletPath 值是否对应一个静态资源
* @param servletPath
* @return
* true:是静态资源
* false:不是静态资源
*/
public static boolean judgeCurrentServletPathWhetherStaticResource(String servletPath) {
if (servletPath == null || servletPath.length() == 0) {
throw new RuntimeException(CrowdConstant.MESSAGE_STRING_INVALIDATE);
}
String[] split = servletPath.split("/");
String firstLevelPath = split[1];
return STATIC_RES_SET.contains(firstLevelPath);
}
}
5.4 Zuul filter
新建zuul\src\main\java\com\atguigu\crowd\filter\CrowdAccessFilter.java
package com.atguigu.crowd.filter;
import com.atguigu.crowd.constant.AccessPassResources;
import com.atguigu.crowd.constant.CrowdConstant;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@Component
public class CrowdAccessFilter extends ZuulFilter {
@Override
public boolean shouldFilter() {
// 1.获取RequestContext对象
RequestContext requestContext = RequestContext.getCurrentContext();
// 2.通过RequestContext对象获取当前请求对象( 框架底层是借助 ThreadLocal 从当前线程上获取事先绑定的 Request 对象)
HttpServletRequest request = requestContext.getRequest();
// 3.获取 servletPath 值
String servletPath = request.getServletPath();
// 4.根据 servletPath 判断当前请求是否对应可以直接放行的特定功能
boolean containsResult = AccessPassResources.PASS_RES_SET.contains(servletPath);
// 如果当前请求是可以直接放行的特定功能请求则返回 false 放行
if (containsResult){
return false;
}
// 5.判断当前请求是否为静态资源
return !AccessPassResources.judgeCurrentServletPathWhetherStaticResource(servletPath);
}
@Override
public Object run() throws ZuulException {
// 1.获取当前请求对象
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
// 2.获取当前Session对象
HttpSession session = request.getSession();
// 3.尝试从 Session 对象中获取已登录用户
Object loginMember = session.getAttribute(CrowdConstant.ATTR_NAME_LOGIN_MEMBER);
// 4.判断 loginMember 是否为空
if (loginMember == null) {
// 5.从requestContext对象中获取Response对象
HttpServletResponse response = requestContext.getResponse();
// 6.将提示消息存入session域
session.setAttribute(CrowdConstant.ATTR_NAME_MESSAGE, CrowdConstant.MESSAGE_ACCESS_FORBIDDEN);
// 7.重定向到 auth-consumer 工程中的登录页面
try {
response.sendRedirect("/auth/member/to/login/page");
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
}
5.5 其余改动
修改authentication-consumer\src\main\resources\templates\member-login.html
<p th:text="${session.message}">在这里显示登录失败的提示消息</p>
5.6 测试
直接输入http://localhost/auth/member/to/login/page或者http://localhost:4000/auth/member/to/login/page,均会被弹出并显示: