微服务商城系统 实战记录 用户、商家、后台管理员注册与登录功能实现
文章目录 现在整个商城系统的后端逻辑基本上都实现了,接下来需要与前端页面联调起来。 用户注册需要以下信息:前端页面:对应后端方法:/**** 新增User数据* @param user* @return*/@PostMappingpublic Result add(@RequestBodyUser use
代码见 https://github.com/betterGa/ChangGou
文章目录
现在整个商城系统的后端逻辑基本上都实现了,接下来需要与前端页面联调起来。
一、用户注册
用户注册需要以下信息:
(注意到 created、updated 都是不为 null 的,所以需要对 UserServiceImpl 的 add 方法进行设置:
public void add(User user){
// 设置创建时间和更新时间为当前时间
Date created=new Date();
Date updated=new Date();
user.setCreated(created);
user.setUpdated(updated);
userMapper.insert(user);
}
)
前端页面:
对应后端方法:
/***
* 新增User数据
* @param user
* @return
*/
@PostMapping
public Result add(@RequestBody User user){
//调用UserService实现添加User
userService.add(user);
return new Result(true,StatusCode.OK,"添加成功");
}
需要先对该路径进行放行:
进行测试:
接下来需要由前端传来数据。前端页面调用后端端口,使用 ajax,
1、使用 ajax (POST 方法)
使用 ajax 之前需要先导入 jquery 依赖,jQuery 是一个 JavaScript 函数库, 是一个轻量级的 “写的少,做的多” 的 JavaScript 库,其中包含 ajax 功能。在 js 文件夹下拷贝 jquery.js 文件,并且把引入 jquery.js 的 放在前面,不能写在一个标签里。
<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript">
整个 script 代码:
<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript">
function entity(username, password, email, phone) {
this.username = username;
this.password = password;
this.email = email;
this.phone = phone;
}
$(function () {
$('#registerBut').on('click', function () {
var username = $('#username').val()
var password = $('#password').val()
var email = $('#email').val()
var phone = $('#phone').val()
returnData = new entity(username, password, email, phone);
$.ajax(
{
url: "http://localhost:18087/user",
type: "POST",
dataType: "JSON",
contentType: "application/json;charset=UTF-8",
data: JSON.stringify(returnData),
success: function (result) {
alert("注册成功!");
},
error: function (result) {
console.log(result);
alert("用户名重复!");
},
cache: false
}
)
}
)
})
</script>
在 jquery 中,$
是常用的一个回传函数,定义为 “选取” , 英文是 selector 的缩写,表示选取。
$.function();
就是 选取 JQuery 定义的 function() 执行。
$('input')
就是 选取 HTML 当中全部的 input 标签。
$('#abc')
就是 选取 HTML 当中 ID 名称为 abc 的物件。
$.fn.testing = function() {}
就是 选取 JQuery 内核函数 fn (函数) 回传给 testing 这个名称、定义为一个功能 function()。
把 script 标签写在 head 之后,body 之前,这样会先加载 script ,再加载页面,因为页面的 “完成登录” 按钮:
<button class="sui-btn btn-block btn-xlarge btn-danger" type="button" target="_blank"
id="registerBut">完成注册</button>
JQuery 的代码通常会包裹在一个 $(function(){})
函数中,$(function(){})
也就是 $(document).ready(function(){})
的简写,与之对应的原生 js 的window.onload 事件。
注意 ,ajax 里面的 data 不能是 json 格式,否则会报 400、415 错误状态码。
2、使用 thymeleaf
访问后端端口时,渲染前端页面,使用 thymeleaf,新建子工程 changgou-web-register,因为父工程 web 工程里已经导入过 thymeleaf 依赖了,所以子工程就不需要导了(实际上清除缓存并重启了一次整个项目之后,thymeleaf 才奏效)
此处踩了个坑,使用 thymeleaf 需要确保 Controller 类 放在与 @SpringBootApplication 注解的启动类 相同包 或者 子包 下。
@Controller
@RequestMapping("/user")
public class RegisterController {
@CrossOrigin
@GetMapping("/register")
public String search() {
return "register";
}
}
集成好 thymeleaf 后,通过后端控制层路径可以访问到前端页面了,但是现在没有加载出样式。
原先 css 路径:
<link rel="stylesheet" type="text/css" href="../static/css/all.css" />
<link rel="stylesheet" type="text/css" href="../static/css/pages-register.css" />
注意,Thymeleaf 使用的是绝对路径,而不是相对路径。
需要把路径修改为:
<link rel="stylesheet" type="text/css" href="/css/all.css" />
<link rel="stylesheet" type="text/css" href="/css/pages-register.css" />
(因为我们在 application.yml 中配置过了:
有空了去掉,再看看效果。)
同样地,jquery 的路径也需要修改:
<script type="text/javascript" src="/js/jquery.js"></script>
至此,通过 http://localhost:18094/user/register 可以访问到用户登录页面。
启动 changgou-web-register,访问 http://localhost:18094/user/register ,这时出现了问题,它弹框 “用户名重复”,而此时到数据库中查看,新增记录是成功的。
根本原因在于,在进行 post 请求之前,进行了一次 options 请求。
众所周知, ajax 请求是由 XMLHttpReques 对象实现的 (部分低版本ID浏览器不是), 而 XMLHttpRequest 会遵守 同源策略(same-origin policy). 也就是说,脚本只能访问 相同协议/相同主机名/相同端口 的资源, 如果要突破这个限制, 那就是所谓的 跨域 , 此时需要遵守 CORS(Cross-Origin Resource Sharing) 机制。也就是说,现在 http://localhost:18094/user/register 的 register.html 代码的 ajax 中,试图访问 http://localhost:18087/user ,跨域了。
而在 W3C 规范中,跨域请求,分为简单请求(GET ;HEAD; POST;除了使用用户代理自动生成的 headers,或者在 Fetch 规范中定义为 “forbidden header name” 的其他 headers,只有以下 headers 被允许用作 人工地设置为 Fetch 规范定义为 “CORS-safelisted request-header” :Accept,Accept-Language,Content-Language,Content-Type;content-type 是 application/x-www-form-urlencoded、multipart 或 text/plain ;XMLHttpRequest对象发出的请求 ) 和复杂请求。而复杂请求发出之前,就会出现一次 options 请求。
(简单请求的定义见 https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#the_http_request_headers:
)
options 请求是一种探测性的请求,通过这个方法,客户端可以在采取具体资源请求之前,决定对该资源采取何种必要措施,或者了解服务器的性能。
在 ajax 中,跨域请求时,json,就属于复杂请求,因此需要提前发出一次 options 请求,用以检查请求是否是可靠安全的,如果 options 获得的回应是拒绝性质的,比如 404\403\500 等 http 状态,就会停止 post、put 等请求的发出。
在代码中, ajax 使用的是跨域请求,而且 contentType 使用的是 application/json,归为复杂请求啦,就会在 POST 方法之前,先使用 OPTIONS ,所以会出现重复提交的情况。
把请求整成不跨域的就OK。
3、解决跨域问题
在 UserController 上使用 @CrossOrigin 支持跨域(这里好像也是需要清除缓存、重新启动项目才能奏效 ),然后进行测试:
现在还有个问题,如果用户名重复的话:
控制台:
后台代码并没有对这样的情况捕获异常,等之后对异常处理熟悉了之后,考虑完善这里的逻辑。
【已解决】在插入数据之前先对数据进行查询,如果已经存在记录,返回 false:
@Override
public boolean add(User user) {
if (userMapper.selectByPrimaryKey(user) == null) {
// 设置创建时间和更新时间为当前时间
Date created = new Date();
Date updated = new Date();
// 设置用户使用状态为正常
user.setStatus("1");
user.setCreated(created);
user.setUpdated(updated);
userMapper.insert(user);
return true;
}
// 如果数据库中已经存在用户名,返回 false
return false;
}
@PostMapping
public Result add(@RequestBody User user){
//调用UserService实现添加User
boolean isInsert=userService.add(user);
if(isInsert){
return new Result(true,StatusCode.OK,"添加成功");}
else return new Result(false,StatusCode.LOGINERROR,"添加失败,用户名重复");
}
在 ajax 中修改逻辑,根据调用接口的返回值进行提示:
$.ajax(
{
url: "http://localhost:18087/user",
type: "POST",
dataType: "JSON",
contentType: "application/json;charset=UTF-8",
data: JSON.stringify(returnData),
success: function (result) {
var flag=result.flag;
if(flag===true){
alert("注册成功!");}
else alert("用户名重复!");
},
error: function (result) {
console.log(result);
alert("访问用户注册接口失败!");
},
cache: false
}
)
二、用户登录
前端登录页面是这样的:
用户登录的逻辑是,到数据库中校验邮箱/用户名/手机号和密码,一致的话,跳转至商城首页。
需要先提供校验的逻辑:
在 mapper 中使用 SQL 语句:
@Select("SELECT * FROM tb_user where (password=#{password} and email=#{param1} or (password=#{password} and phone=#{param1} or (password=#{password} and username=#{param1})")
void registerCheck(String param1, String password);
服务层:
@Override
public User registerCheck(String param1, String password) {
List<User> users = userMapper.registerCheck(param1, password);
// 查询无果
if (users == null || users.size() == 0) return null;
else return users.get(0);
}
控制层:
@GetMapping("/login")
public Result registerCheck(@RequestParam(value = "param") String param1,@RequestParam("password") String password) {
User user = userService.registerCheck(param1,password);
if (user == null) {
return new Result(false, StatusCode.ERROR, "用户不存在");
} else {
return new Result(true, StatusCode.OK, "用户登录校验成功", user);
}
}
测试结果:
需要对该路径进行放行:
1、ajax 使用 GET 方法
使用 ajax 调用该 GET 方法,并把参数传过去,data 要求为 Object 或 String 类型的参数,发送到服务器的数据。如果已经不是字符串,将自动转换为字符串格
式。get 请求中将附加在 url 后。防止这种自动转换,可以查看 processData 选项。对象必须为key/value 格式,例如 {foo1:“bar1”,foo2:“bar2”} 转换为 &foo1=bar1&foo2=bar2 。如果是数组,JQuery 将自动为不同值对应同一个名称。例如 {foo:[“bar1”,“bar2”]} 转换为 &foo=bar1&foo=bar2。
设置登录按钮:
<button class="sui-btn btn-block btn-xlarge btn-danger" target="_blank"
id="loginBut" type="button">登 录</button>
注意:这里的 type=button 是必须的,否则后续点击后无法完成跳转到其他页面。
使用 ajax 访问登录接口:
<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript">
$(function () {
$('#loginBut').on('click', function () {
$.ajax(
{
url: "http://localhost:18087/user/login",
type: "GET",
dataType: "JSON",
contentType: "application/json;charset=UTF-8",
data: {param: $('#inputName').val(), password: $('#inputPassword').val()},
success: function (result) {
var flag = result.flag;
if (flag === true) {
var status = result.data.status;
if (status == 1) {
alert("登录校验成功!即将跳转至商城首页");
window.location.href = "http://localhost:18086/search/list";
} else {
alert("用户使用状态异常,已被限制访问");
}
} else alert("用户不存在,请重新登录或注册!");
},
error: function (result) {
console.log(result);
alert("访问用户登录接口失败!");
},
cache: false
}
)
})
})
</script>
三、商家注册
前端页面:
1、dao、service、controller 层搭建
数据库建表:
CREATE TABLE `tb_store` (
`storename` varchar(50) NOT NULL COMMENT '商家名称',
`password` varchar(100) NOT NULL COMMENT '商家密码',
`invitecode` varchar(50) NOT NULL COMMENT '平台邀请码',
`centerstatus` varchar(1) DEFAULT NULL COMMENT '入驻状态。1表示允许入驻,0表示未入驻',
`created` datetime DEFAULT NULL COMMENT '创建时间',
`updated` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`storename`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
dao 层继承通用 mapper:
public interface StoreMapper extends Mapper<Store> {
}
服务层实现:
@Service
public class StoreServiceImpl implements StoreService {
@Autowired
StoreMapper storeMapper;
@Override
public boolean add(Store store) {
Store storeResult = storeMapper.selectByPrimaryKey(store);
if (storeResult == null) {
Date date = new Date();
store.setUpdated(date);
store.setCreated(date);
// 设置状态未入驻
store.setCenterStatus("0");
storeMapper.insert(store);
// 插入成功
return true;
}
// 说明商户名已存在,返回插入失败
return false;
}
控制层:
@RestController
@RequestMapping("/store")
public class StoreController {
@Autowired
StoreService storeService;
@CrossOrigin
@PostMapping("/register")
public Result register(@RequestBody Store store) {
boolean isSuccess = storeService.add(store);
if (isSuccess) {
return new Result(true, StatusCode.OK, "插入商家记录成功");
} else return new Result(false, StatusCode.ERROR, "用户名已存在,插入失败");
}
测试结果:
ajax 逻辑:
$.ajax(
{
url: "http://localhost:18089/store/register",
type: "POST",
dataType: "JSON",
contentType: "application/json;charset=UTF-8",
data: JSON.stringify(returnData),
success: function (result) {
var flag=result.flag;
if(flag===true){
alert("注册成功!");}
else alert("商家名重复!");
},
error: function (result) {
console.log(result);
alert("访问商家注册接口失败!");
},
cache: false
}
)
四、商家登录
前端页面:
服务层实现:
@Override
public Store checkLogin(String storeName, String password) {
Store store=new Store();
store.setStoreName(storeName);
store.setPassword(password);
List<Store> storeList=storeMapper.select(store);
// 查询无果
if(storeList==null||storeList.size()==0){
return null;
}else {
return storeList.get(0);}
}
控制层:
@CrossOrigin
@GetMapping("/login")
public Result login(@RequestParam(value = "storeName") String storename,
@RequestParam(value = "password") String password) {
Store store=storeService.checkLogin(storename, password);
if (store!=null) {
return new Result(true, StatusCode.OK, "登录成功!",store);
} else {
return new Result(false, StatusCode.ERROR, "登录失败,查询无果!");
}
}
测试结果:
前端调用 ajax 逻辑:
$.ajax(
{
url: "http://localhost:18089/store/login",
type: "GET",
dataType: "JSON",
contentType: "application/json;charset=UTF-8",
data: {storeName: $('#storeName').val(), password: $('#inputPassword').val()},
success: function (result) {
var flag = result.flag;
if (flag === true) {
var centerStatus = result.data.centerStatus;
if (centerStatus == 1) {
alert("登录校验成功!即将跳转至商家管理页面");
window.location.href = "http://localhost:18086/search/list";
} else {
alert("商家未入驻,需等待管理员审核");
}
} else alert("商家不存在,请重新登录或注册!");
},
error: function (result) {
console.log(result);
alert("访问用户登录接口失败!");
},
cache: false
}
)
五、后台管理员登录
不在后端代码中做逻辑判断,只是在 js 里判断用户名和密码是否同时是 “admin”,是的话登录成功,后续跳转到管理页面,否则提示登录失败。
<script type="text/javascript">
function login(username, password) {
if (username === 'admin' && password === 'admin') {
alert('登录成功,即将进入管理页面');
//window.location.href('');
} else alert('请输入正确的管理员用户名和密码');
}
</script>
更多推荐
所有评论(0)