引言
本文代码已提交至Github(版本号:
8f3a993f0f9b92071437d16ed290e27ac1c64cde
),有兴趣的同学可以下载来看看:https://github.com/ylw-github/taodong-shop
在上一节《淘东电商项目(25) -门户注册功能》,主要讲解了注册的整个流程(前端+后台)。
本文主要讲解前端与后台交互部分,实现登录的整个流程(前端+后端)。
本文目录结构:
l____引言
l____ 1. 登录
l________ 1.1 前端代码
l________ 1.2 后端代码
l____ 2. 登录成功主页面显示
l________ 2.1 前端代码
l________ 2.2 后端代码
l____ 3. 测试
l____总结
1. 登录
1.1 前端代码
1.前端界面:
2.界面HTML代码:
<div class="layui-fulid" id="house-login">
<form action="login" method="post">
<div class="layui-form">
<p>手机号登录</p>
<div class="layui-input-block login">
<i class="layui-icon layui-icon-username"></i> <input type="text"
required lay-verify="required" name="mobile"
value="${(loginVo.mobile)!''}"
placeholder="请输入手机号码"
class="layui-input">
</div>
<div class="layui-input-block login">
<i class="layui-icon layui-icon-vercode"></i> <input
type="password" required lay-verify="required" name="password"
value="${(loginVo.password)!''}" placeholder="请输入密码"
class="layui-input">
</div>
<div class="layui-input-block getCode">
<input type="text" name="graphicCode" required lay-verify="required"
placeholder="请输入验证码" class="layui-input"> <img alt=""
src="getVerify" onclick="getVerify(this);"
style="border: 1px solid #e2e2e2;font-size: 18px;height: 48px;margin-top: -93px;width: 44%;background-color: #e8d6c0;margin-left: 166px;">
</div>
<span
style="color: red; font-size: 20px; font-weight: bold; font-family: '楷体', '楷体_GB2312';">${error!''}</span>
<button class="layui-btn" style="margin-top: 5px;" lay-submit lay-filter="user-login">登录</button>
</div>
</form>
</div>
1.2 后端代码
1.登录VO实体类:
@Data
public class LoginVo {
/**
* 手机号码
*/
@NotBlank(message = "手机号码不能为空")
@Size(min = 11, max = 11, message = "手机号码长度不正确")
@Pattern(regexp = "^(((13[0-9])|(14[579])|(15([0-3]|[5-9]))|(16[6])|(17[0135678])|(18[0-9])|(19[89]))\\d{8})$", message = "手机号格式错误")
private String mobile;
/**
* 密码
*/
@NotNull(message = "密码不能为空!")
private String password;
/**
* 验证码
*/
@NotNull(message = "密码不能为空!")
private String graphicCode;
}
2.Cookie工具类:
/**
* description: Cookie工具类
* create by: YangLinWei
* create time: 2020/3/10 5:27 下午
*/
public final class CookieUtils {
/**
* 得到Cookie的值, 不编码
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName) {
return getCookieValue(request, cookieName, false);
}
/**
* 得到Cookie的值,
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null) {
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
if (isDecoder) {
retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
} else {
retValue = cookieList[i].getValue();
}
break;
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return retValue;
}
/**
* 得到Cookie的值,
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null) {
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
break;
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return retValue;
}
/**
* 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue) {
setCookie(request, response, cookieName, cookieValue, -1);
}
/**
* 设置Cookie的值 在指定时间内生效,但不编码
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage) {
setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);
}
/**
* 设置Cookie的值 不设置生效时间,但编码
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, boolean isEncode) {
setCookie(request, response, cookieName, cookieValue, -1, isEncode);
}
/**
* 设置Cookie的值 在指定时间内生效, 编码参数
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage, boolean isEncode) {
doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);
}
/**
* 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage, String encodeString) {
doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);
}
/**
* 删除Cookie带cookie域名
*/
public static void deleteCookie(HttpServletRequest request, HttpServletResponse response, String cookieName) {
doSetCookie(request, response, cookieName, "", -1, false);
}
/**
* 设置Cookie的值,并使其在指定时间内生效
*
* @param cookieMaxage
* cookie生效的最大秒数
*/
private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage, boolean isEncode) {
try {
if (cookieValue == null) {
cookieValue = "";
} else if (isEncode) {
cookieValue = URLEncoder.encode(cookieValue, "utf-8");
}
Cookie cookie = new Cookie(cookieName, cookieValue);
if (cookieMaxage > 0)
cookie.setMaxAge(cookieMaxage);
if (null != request) {// 设置域名的cookie
String domainName = getDomainName(request);
System.out.println(domainName);
if (!"localhost".equals(domainName)) {
// cookie.setDomain(domainName);
}
}
cookie.setPath("/");
response.addCookie(cookie);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 设置Cookie的值,并使其在指定时间内生效
*
* @param cookieMaxage
* cookie生效的最大秒数
*/
private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage, String encodeString) {
try {
if (cookieValue == null) {
cookieValue = "";
} else {
cookieValue = URLEncoder.encode(cookieValue, encodeString);
}
Cookie cookie = new Cookie(cookieName, cookieValue);
if (cookieMaxage > 0)
cookie.setMaxAge(cookieMaxage);
if (null != request) {// 设置域名的cookie
String domainName = getDomainName(request);
System.out.println(domainName);
if (!"localhost".equals(domainName)) {
// 本地测试的时候不要写.实际发布时在打开
// cookie.setDomain(domainName);
}
}
cookie.setPath("/");
response.addCookie(cookie);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 得到cookie的域名
*/
private static final String getDomainName(HttpServletRequest request) {
String domainName = null;
String serverName = request.getRequestURL().toString();
if (serverName == null || serverName.equals("")) {
domainName = "";
} else {
final int end = serverName.lastIndexOf("/");
serverName = serverName.substring(0, end);
final String[] domains = serverName.split("\\.");
int len = domains.length;
if (len > 3) {
// www.xxx.com.cn
domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
} else if (len <= 3 && len > 1) {
// xxx.com or xxx.cn
domainName = "." + domains[len - 2] + "." + domains[len - 1];
} else {
domainName = serverName;
}
}
if (domainName != null && domainName.indexOf(":") > 0) {
String[] ary = domainName.split("\\:");
domainName = ary[0];
}
return domainName;
}
}
3.登录Controller:
/**
* description: 登录页
* create by: YangLinWei
* create time: 2020/3/5 1:48 下午
*/
@Controller
public class LoginController extends BaseWebController {
/**
* 跳转到登陆页面页面
*/
private static final String MB_LOGIN_FTL = "member/login";
@Autowired
private MemberLoginServiceFeign memberLoginServiceFeign;
/**
* 重定向到首页
*/
private static final String REDIRECT_INDEX = "redirect:/";
/**
* 跳转页面
*
* @return
*/
@GetMapping("/login")
public String getLogin() {
return MB_LOGIN_FTL;
}
/**
* 接受请求参数
*
* @return
*/
@PostMapping("/login")
public String postLogin(@ModelAttribute("loginVo") @Validated LoginVo loginVo,
BindingResult bindingResult, Model model, HttpServletRequest request,
HttpServletResponse response, HttpSession httpSession) {
if (bindingResult.hasErrors()) {
// 如果参数有错误的话
// 获取第一个错误!
String errorMsg = bindingResult.getFieldError().getDefaultMessage();
setErrorMsg(model, errorMsg);
return MB_LOGIN_FTL;
}
// 1.图形验证码判断
String graphicCode = loginVo.getGraphicCode();
if (!RandomValidateCodeUtil.checkVerify(graphicCode, httpSession)) {
setErrorMsg(model, "图形验证码不正确!");
return MB_LOGIN_FTL;
}
// 2.将vo转换dto调用会员登陆接口
UserLoginInDTO userLoginInpDTO = WebBeanUtils.voToDto(loginVo, UserLoginInDTO.class);
userLoginInpDTO.setLoginType(Constants.MEMBER_LOGIN_TYPE_PC);
String info = webBrowserInfo(request);
userLoginInpDTO.setDeviceInfor(info);
BaseResponse<JSONObject> login = memberLoginServiceFeign.login(userLoginInpDTO);
if (!isSuccess(login)) {
setErrorMsg(model, login.getMsg());
return MB_LOGIN_FTL;
}
// 3.登陆成功之后如何处理 保持会话信息 将token存入到cookie 里面 首页读取cookietoken 查询用户信息返回到页面展示
JSONObject data = login.getData();
String token = data.getString("token");
CookieUtils.setCookie(request, response, WebConstants.LOGIN_TOKEN_COOKIENAME, token);
return REDIRECT_INDEX;
}
}
2. 登录成功主页面显示
2.1 前端代码
修改head.ftl(代码片段):
<span class="layui-breadcrumb" lay-separator="|">
<#if desensMobile??>
<a href="">${desensMobile}</a>
<a href="">我的订单</a>
<a href="/exit">退出</a>
<#else >
<a href="login">登录</a>
</#if>
<a href="">我的订单</a>
<a href="">在线客服</a>
</span>
2.2 后端代码
直接贴上Controller代码:
@Controller
public class IndexController extends BaseWebController {
@Autowired
private MemberServiceFeign memberServiceFeign;
/**
* 跳转到index页面
*/
private static final String INDEX_FTL = "index";
@RequestMapping("/")
public String index(HttpServletRequest request, HttpServletResponse response, Model model) {
// 1.从cookie 中 获取 会员token
String token = CookieUtils.getCookieValue(request, WebConstants.LOGIN_TOKEN_COOKIENAME, true);
if (!StringUtils.isEmpty(token)) {
// 2.调用会员服务接口,查询会员用户信息
BaseResponse<UserOutDTO> userInfo = memberServiceFeign.getInfo(token);
if (isSuccess(userInfo)) {
UserOutDTO data = userInfo.getData();
if (data != null) {
String mobile = data.getMobile();
// 对手机号码实现脱敏
String desensMobile = mobile.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
model.addAttribute("desensMobile", desensMobile);
}
}
}
return INDEX_FTL;
}
}
3. 测试
当前数据库已经注册的用户如下:
登录(手机号:13800000001
,密码:123456
):
可以看到Redis和数据库均显示登录成功:
Redis | 数据库 |
---|---|
![]() |
![]() |
同时主界面已经有了回显了:
总结
本文主要讲解登录的完整流程(前端+后台)。关键点是Controller处理的流程,登录控制层LoginController使用CookieUtils
保存token
,主页控制层IndexContoller主要通过获取客户端的token
来查询用户信息。