实现登录2.1
设计数据库
id:用户ID,是用户的手机号
nickname:名称
password:密码(经过MD5加密的=(pass明文+固定salt)+salt)
salt:盐值
head:头像
registerDate:注册时间
lastLoginDate:上次登录时间
loginCount:登录次数
两次MD5
第一次MD5是为避免明文密码在传输过程中被抓包使秘密被暴露。
第二次MD5是为避免数据库被截取后用反查,将密文密码反推出明文密码。
基础实现
- 添加MD5的依赖
<!--MD5-->
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
<!--lang3-->
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
- 在启动类同包下创建util包,在其中创建MD5Util,在里面创建MD5方法,先进行对明文进行MD5,在commons-codec包中自带MD5util加密算法,在DigestUtils对象中。规定一个固定的salt,用于处理用户输入的密码,将其与用户输入的密码按照自定义规则进行拼接,将拼接过的密码传递给md5方法进行加密,传递给客户端。
public static String md5(String src){
return DigestUtils.md5Hex(src);
}
public static String salt = "1a2b3c4d";
public static String inputPassToFormPass(String inputPass){
String str = "" + salt.charAt(0) + salt.charAt(2) + inputPass + salt.charAt(5) + salt.charAt(4);
System.out.println(str);
return md5(str);
}
- 以上为第一次的MD5,第二层次的MD5是将form的password进行MD5加密。创建fromPassToDBPass方法,在此方法中,从前台接收到的password和随机的salt,按照自定义规则进行拼接,通过md5方法进行加密。
public static String formPassToDBPass(String formPass, String salt){
String str = "" + salt.charAt(0) + salt.charAt(2) + formPass + salt.charAt(5) + salt.charAt(4);
return md5(str);
}
- 写一个将用户输入的密码直接转换为存在数据库中的密码的方法,第一步是将明文密码转化为form密码,第二步是将form密码转换为数据库存储的密码。
public static String inputPassToDBPass(String inputPass, String saltDB){
String formPass = inputPassToFormPass(inputPass);
String dbPass = formPassToDBPass(formPass, saltDB);
return dbPass;
}
- 为MD5Until创建一个主函数,在函数中调用inputPassToDBPass方法,可以在控制台得到一个经过2次MD5加密的字符串
- 根据MD5Until中调用inputPassToDBPass方法时使用的salt和得到的结果,在数据库中创建一个新的对象。
完成页面
在搭建好SpringBoot+mybatis+redis后,为登录创建一个专属的Controller,并在其中创建进入登录页面的toLogin方法和实现登录的doLogin方法。
- 在resources包下的 templates中创建login.html
- 向resources包中导入static包。其中的bootstrap用于绘制页面,jquery-validation用于错误表单验证,js中common.js是我们自己的js,layer.中的layer.js用于做弹框。
common.js中的写在加载中时出现的提示和第一次Md5加密时的salt
//展示loading
function g_showLoading(){
var idx = layer.msg('处理中...', {icon: 16,shade: [0.5, '#f5f5f5'],scrollbar: false,offset: '0px', time:100000}) ;
return idx;
}
//salt
var g_passsword_salt="1a2b3c4d"
- layer.msg用与配置弹窗,layer.msg(‘弹窗提示的内容’,{icon图标,shade遮罩[透明度,颜色],offset坐标,time自动关闭所需毫秒})
- 进入templates下的login.html中,进行静态文件的加载。thymeleaf引入静态文件,用@+{这个文件在static包下的路径}。
- 基于bootstrap编写页面,实现点击登录后会进行的事情。在login函数中先验证一下form表单,如果验证通过使用doLogin函数。
$("#loginForm").validate({
submitHandler:function(form){
doLogin();
}
});
在doLogin函数中,先用g_showLoading加载一个lodaing,再实现用户输入密码的第一次Md5加密。
var inputPass = $("#password").val(); <!-- 用户输入的明文密码 -->
var salt = g_passsword_salt;
var str = ""+salt.charAt(0)+salt.charAt(2) + inputPass +salt.charAt(5) + salt.charAt(4);<!-- 要和后台规则一样 -->
var password = md5(str); <!-- 第一次加密后的密码 -->
用ajax实现登录:
$.ajax({
url: "/login/do_login",
type: "POST",
data:{
mobile:$("#mobile").val(),
password: password
},
success:function(data){
layer.closeAll();
// console.log(data);
if(data.code == 0){ //服务端Result类中定义的code
layer.msg("成功"); //会在屏幕上弹出一个短暂的提示信息,并自动消失
window.location.href="/goods/to_list"; //登录成功后的跳转在这里实现
}else{
layer.msg(data.msg); //服务端Result类中定义的msg
}
},
error:function(){
layer.closeAll();
}
});
- 新建login.xml,在head标签中引入静态文件后,在body标签中创建表单loginForm,在按照规划将表单创建完后,开始写script,在script标签中写两个function,分别为用于验证表单中信息的login和实现登录业务逻辑的doLogin
<script>
function login(){//验证一下form表单中的东西
$("#loginForm").validate({
submitHandler:function(form){
doLogin();
}
});
}
function doLogin(){
//展示一个加载中
g_showLoading();
var inputPass = $("#password").val(); <!-- 用户输入的明文密码 -->
var salt = g_passsword_salt;
var str = ""+salt.charAt(0)+salt.charAt(2) + inputPass +salt.charAt(5) + salt.charAt(4);
var password = md5(str); <!-- 第一次加密后的密码 -->
$.ajax({
url: "/login/do_login",
type: "POST",
data:{
mobile:$("#mobile").val(),
password: password
},
success:function(data){
layer.closeAll();
console.log(data);
if(data.code == 0){ //服务端Result类中定义的code
layer.msg("成功"); //会在屏幕上弹出一个短暂的提示信息,并自动消失
}else{
layer.msg(data.msg); //服务端Result类中定义的msg
}
},
error:function(){
layer.closeAll();
}
});
}
</script>
- 编写LoginController,在其中用于进入登录页面的login方法,它通过返resources中templates下的login.xml文件的名称,使我们进入登录页面。再编写doLogin方法中的内容,用于实现登录,主要操作为判断从前端收到的数据的合法性和规范性和对数据库中是否有此用户的查询。
@RequestMapping("/do_login")
@ResponseBody
public Result<Boolean> doLogin(LoginVo loginVo){
log.info(loginVo.toString());
//参数校验
String passInput = loginVo.getPassword();//密码是否为空
String mobile = loginVo.getMobile();//判断手机号的格式
if(StringUtils.isEmpty(passInput)){
return Result.error(CodeMsg.PASSWORD_EMPTY);
}
if(StringUtils.isEmpty(mobile)){
return Result.error(CodeMsg.MOBILE_EMPTY);
}
if(!ValidatorUtil.isMobile(mobile)){
return Result.error(CodeMsg.MOBILE_ERROR);
}
return null;
}
其中的ValidatorUtil,是一个自定义对象,主要运用检验电话号码的合法性
public class ValidatorUtil {
private static final Pattern mobile_pattern = Pattern.compile("1\\d{10}"); //定义规则:以1开头,后面跟10个数字
public static boolean isMobile(String src){
if (StringUtils.isEmpty(src)){
return false;
}
Matcher matcher = mobile_pattern.matcher(src);
return matcher.matches();
}
}
- 创建秒杀数据库中用户信息表,并在对应的domain中创建其映射类MiaoshaUser,在方法的上面添加@Data注解,就可以不用创建各属性的set和get方法了。
- 进入dao层,为MiaoshaUser创建mapper,在MiaoshaUserMapper接口中创建getById方法用于判断接收controller逐层传递的用户信息是否存在与对应数据库中
@Mapper
public interface MiaoshaUserMapper {
@Select("select * from miaosha_user where id = #{id}")
public MiaoshaUser getById(@Param("id") long id); //入参为long类型
}
- 创建MiaoshaUserService类,并在其中创建getById方法。创建login方法和getById方法。在getById中调用对应Mapper中的查询方法,在login中先判断下所收到的参数是否不为空,若不为空就判断手机号是否存在,因为在数据库中id保存到就是手机号码,所以调用getById查看数据库中有没有这个手机号。若这个手机号存在则进行密码验证,dbpass是对应用户在数据表中的密码,使用从数据库中找到的对应用户的salt,给传入的password进行MD5加密,再判断加密后的字符串是否与dbpass相等。
public MiaoshaUser getById(long id){
return miaoshaUserMapper.getById(id);
}
public CodeMsg login(LoginVo loginVo) {
if(loginVo == null){
return CodeMsg.SERVER_ERROR;
}
String mobile = loginVo.getMobile();
String formPass = loginVo.getPassword();
//判断手机号是否存在
MiaoshaUser user = getById(Long.parseLong(mobile));
if(user == null){
return CodeMsg.MOBILE_NOT_EXIST;
}
//验证密码
String dbPass = user.getPassword();
String saltDB = user.getSalt();
String calcPass = MD5Util.formPassToDBPass(formPass, saltDB);
if (!calcPass.equals(dbPass)){
return CodeMsg.PASSWORD_ERROR;
}
return CodeMsg.SUCCESS;
}
- 运行测试
使用github提交时遇到的问题
Github在idea登录用户中出现Invalid authentication data. Connection reset
在server的git.com前添加https://,如果还是不可以,要多试几次
在idea中commit代码,卡在了performing code analysis阶段
进入setting找到CommitDiolag,把"Perform code analysis" 和 “Check TODO” 复选框前面的勾去掉
在idea上commit代码后,在github中却不能看到新提交的代码
因为只是提交到了本地仓库中,还需要想github官网进行提交
右击项目名称,找到git的Repository,点击其中的push,出现push commit弹窗,点击push提交成功就可以在官网上看到。
进入官网:
mysql不能连接localhost
可能是服务停止运行了,进入计算机的任务管理器下的服务里找mysql,若是停止的状态,就将它开启。