1.手机快速登录
手机快速登录功能,就是通过短信验证码的方式进行登录。这种方式相对于用户名密码登录方式,用户不需要记忆自己的密码,只需要通过输入手机号并获取验证码就可以完成登录,是目前比较流行的登录方式。
前端页面:
<!-- 页面内容 --><div class="contentBox"><div class="login"><form id='login-form'><div class="input-row"><label>手机号</label><div class="loginInput"><input v-model="loginInfo.telephone" id='account' type="text" placeholder="请输入手机号"><input id="validateCodeButton" @click="sendValidateCode()" type="button" style="font-size: 12px" value="获取验证码"></div></div><div class="input-row"><label>验证码</label><div class="loginInput"><input v-model="loginInfo.validateCode" style="width:80%" id='password' type="password" placeholder="请输入验证码"></div></div><div class="input-row" style="font-size: 12px"><input type="checkbox" checked>阅读并同意《瑞通健康用户协议》《瑞通健康隐私条款》</div><div class="btn yes-btn"><a @click="login()" href="#">登录</a></div></form></div></div>
//发送验证码sendValidateCode(){if (this.loginInfo.telephone==undefined){this.$message.error("手机号为空!")return;}ref=/^1[3-9][0-9]{9}$/;if (!ref.test(this.loginInfo.telephone)){this.$message.error("请输入格式正确的手机号!")return;}let num=30;let btn=$("#validateCodeButton");let id=setInterval(function (){num--;btn.attr("disabled",true)btn.val(num+"秒后再次发送!")if (num==0){btn.attr("disabled",false)btn.val("获取验证码")clearInterval(id)}},1000)axios.get("/LoginController/loginPhone.do?telephone="+this.loginInfo.telephone).then(response=>{if (response.data.flag){console.log(response.data.data)}})},
依旧使用正则表达式判断输入的正确性,每个判断后使用return避免提示被击穿!
选择按钮也是用jQuery实现选择;
SetInterVal计时器实现倒计时;
后端:
@GetMapping("/loginPhone")public Result loginPhone(String telephone){Integer capstr= ValidateCodeUtils.generateValidateCode(4);Jedis jedis=jedisPool.getResource();jedis.setex(telephone+ RedisConstant.SENDTYPE_ORDER,60*60*60, String.valueOf(capstr));jedis.close();return new Result(true,"success",capstr);}
生成验证码,使用暂存存入Redis,键位手机号加上预约类型,时间为秒计时,验证码由工具类生成;
结束关闭Jedis;(省略验证码环节!)
前端登陆判断:
login(){if (this.loginInfo.telephone==undefined){this.$message.error("手机号为空!")return;}ref=/^1[3-9][0-9]{9}$/;if (!ref.test(this.loginInfo.telephone)){this.$message.error("请输入格式正确的手机号!")return;}if (this.loginInfo.validateCode==undefined){this.$message.error("请输入验证码!")return;}axios.post("/LoginController/loginJudge.do",this.loginInfo).then(response=>{if (response.data.flag){this.$message.success(response.data.message)}else {this.$message.error(response.data.message)}})}
后端:
@PostMapping("/loginJudge")public Result loginJudge(@RequestBody Map<String ,Object> objectMap, HttpServletResponse response){String telephone= (String) objectMap.get("telephone");String validateCode = (String) objectMap.get("validateCode");Jedis jedis = jedisPool.getResource();//手机号+Redis的常量为键,取出Redis数据中保存的验证码String redisCode = jedis.get(telephone+ RedisConstant.SENDTYPE_ORDER);if (redisCode == null){//验证失败return new Result(false, "验证码过期");}//判断验证码是否相等if (!validateCode.equals(redisCode)){//验证失败return new Result(false, MessageConstant.VALIDATECODE_ERROR);}Cookie cookie=new Cookie("telphone",telephone);cookie.setMaxAge(60*60*24*60);cookie.setPath("/");response.addCookie(cookie);Member a =memberMapper.ifMember(telephone);Result result = null;if (a==null){a=new Member();a.setPhoneNumber(telephone);int res= memberMapper.addMember(a);if (res!=0){result= new Result(true,"会员自动注册成功!");}else {result= new Result(false,"会员自动注册失败!");}}else {result=new Result(true,"会员登录!");}return result;}
1.通过手机号,判断Redis的验证码对比是否一样;
2.设置Cookie,增加用户体验,以便下次登陆时手机号自动添加;
将Cookie返回前端后,前端是键值对形式,需要切割使用,前端在挂载时拿到数据,就可以先一步显示;
mounted(){let cookie = document.cookie.split("=")[1];console.log(cookie)if (cookie != undefined){// $("#account").val(cookie);document.getElementById("account").value = cookie//发送请求,提交cookie == 手机号this.loginInfo.telephone = cookie}
}
Cookie 是一种小型数据文件,通常由网站在用户的浏览器中存储,用于多种目的。以下是 cookie 的一些主要作用:
会话管理:Cookie 可以存储用户的登录状态,方便用户在网站上保持登录,免去每次访问时都要输入用户名和密码的麻烦。
个性化设置:网站可以通过 Cookie 保存用户的偏好设置,比如语言、主题、布局等,提供更个性化的浏览体验。
追踪和分析:Cookie 可以用于收集用户的浏览行为数据,帮助网站分析流量、优化内容和改进用户体验。
广告投放:许多网站使用 Cookie 来记录用户的兴趣和行为,以便为其提供更相关的广告内容。
购物车功能:在电商网站上,Cookie 可以帮助记住用户在购物车中添加的商品,即使用户离开网站,重新访问时也能找到之前的选择。
总之,Cookie 在提升用户体验、提供个性化服务和帮助网站运营方面发挥着重要作用。
2.权限控制
现阶段Java框架实现分权限登录至少需要五张表
第一张表是用户表t_user ,第二张表是角色表t_role ,第三张表是权限表t_permission ,因为用户与角色是多对多,角色和权限是多对多,所以我们还想需要两张中间表,以上;
本案例使用Spring Security来实现权限管理;
1.创建health_Security模块,使用webapp模板,配置文件参考spring MVC;
提供index.html页面,内容为hello Spring Security!!
2.在spring-security.xml中主要配置Spring Security的拦截规则
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xmlns:security="http://www.springframework.org/schema/security"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security.xsd
">
<context:component-scan base-package="com.home"/><mvc:annotation-driven><mvc:path-matching suffix-pattern="true"/></mvc:annotation-driven><!--配置 security 要求拦截的访问资源pattern 拦截地址access hasRole('ADMIN') 需要角色,管理员角色,不拦截--><!--匿名访问,不拦截--><security:http security="none" pattern="/a.html"/><security:http security="none" pattern="/login.html"/><!--intercept-url:定义一个拦截规则pattern:对哪些url进行权限控制access:在请求对应的URL时需要什么权限,默认配置时它应该是一个以逗号分隔的角色列表,请求的用户只需拥有其中的一个角色就能成功访问对应的URL--><security:http auto-config="true" use-expressions="true"><security:intercept-url pattern="/index.jsp" access="hasRole('ROLE_ADMIN')"></security:intercept-url><security:form-login login-page="/login.html" default-target-url="/a.html"authentication-failure-url="/login.html"username-parameter="username" password-parameter="password" login-processing-url="/login.do"/><security:csrf disabled="true"></security:csrf></security:http><!--认证管理器--><security:authentication-manager><!--自定义登录认证--><security:authentication-provider user-service-ref="userSecurity"><!--配置登录的账户和密码,框架提供登录页面的-->
<!-- <security:user-service>--><!--登录账户的名字 admin,角色admin,密码{noop}密码是明文的,不加密的密码-->
<!-- <security:user name="admin" authorities="ROLE_ADMIN" password="{noop}admin"/>-->
<!-- </security:user-service>--></security:authentication-provider></security:authentication-manager><bean class="com.home.security.UserSecurity" id="userSecurity"></bean></beans>
1、项目中我们将所有的资源(所有请求URL)都保护起来,实际环境下往往有一些资源不需要认证也可以访问,也就是可以匿名访问。
<!--http:用于定义相关权限控制指定哪些资源不需要进行权限校验,可以使用通配符
-->
<security:http security="none" pattern="/pages/a.html" />
<security:http security="none" pattern="/paegs/b.html" />
<security:http security="none" pattern="/pages/**"></security:http>
通过上面的配置可以发现,pages目录下的文件可以在没有认证的情况下任意访问。
2、登录页面是由框架生成的,而我们的项目往往会使用自己的登录页面。
<html>
<head><title>登录</title>
</head>
<body><form action="/login.do" method="post">username:<input type="text" name="username"><br>password:<input type="password" name="password"><br><input type="submit" value="submit"></form>
</body>
</html>
第二步:修改spring-security.xml文件,指定login.html页面可以匿名访问
<security:http security="none" pattern="/login.html" />
第三步:修改spring-security.xml文件,加入表单登录信息的配置
<security:http auto-config="true" use-expressions="true"><security:intercept-url pattern="/index.jsp" access="hasRole('ROLE_ADMIN')"></security:intercept-url><security:form-login login-page="/login.html" default-target-url="/a.html"authentication-failure-url="/login.html"username-parameter="username" password-parameter="password" login-processing-url="/login.do"/><security:csrf disabled="true"></security:csrf></security:http>
3、直接将用户名和密码配置在了配置文件中,而真实生产环境下的用户名和密码往往保存在数据库中。
在后端取用数据库的数据
本案例使用Map模拟
public class UserSecurity implements UserDetailsService {private Map<String, User> map = new HashMap<>();public void initdata(){map.put("viki",new User("viki","123"));map.put("jiajun",new User("jiajun","456"));}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {initdata();//用户名为 键,取出Map中的集合User user = map.get(username);if(user == null){return null;}//取出密码String password ="{noop}"+ user.getPw();//返回UserDetails 对象//User对象构造方法,用户名,密码,授权数据//授权数据:Collection<? extends GrantedAuthority> authorities)List<GrantedAuthority> list = new ArrayList<>();//集合添加GrantedAuthority子类对象list.add(new SimpleGrantedAuthority("ROLE_ADMIN"));UserDetails userDetails = new org.springframework.security.core.userdetails.User(username,password,list);return userDetails;}
}
4、在配置文件中配置的密码使用明文,这非常不安全,而真实生产环境下密码需要进行加密。
前面我们使用的密码都是明文的,这是非常不安全的。一般情况下用户的密码需要进行加密后再保存到数据库中。
常见的密码加密方式有:
3DES、AES、DES:使用对称加密算法,可以通过解密来还原出原始密码
MD5、SHA1:使用单向HASH算法,无法通过计算还原出原始密码,但是可以建立彩虹表进行查表破解
bcrypt:将salt随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理salt问题
加密后的格式一般为:
$2a$10$/bTVvqqlH9UiE0ZJZ7N2Me3RIgUCdgMheyTgV0B4cMCSokPa.6oCa
加密后字符串的长度为固定的60位。其中:$是分割符,无意义;2a是bcrypt加密版本号;10是cost的值;而后的前22位是salt值;再然后的字符串就是密码的密文了。
配置文件:
public class MD5Utils {/*** 使用md5的算法进行加密*/public static String md5(String plainText) {byte[] secretBytes = null;try {secretBytes = MessageDigest.getInstance("md5").digest(plainText.getBytes());} catch (NoSuchAlgorithmException e) {throw new RuntimeException("没有md5这个算法!");}String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字// 如果生成数字未满32位,需要前面补0for (int i = 0; i < 32 - md5code.length(); i++) {md5code = "0" + md5code;}return md5code;}
}
public class TestMD5 {public static void main(String[] args) {String string = MD5Utils.md5("shisong123");//21232f297a57a5a743894a0e4a801fc3 == admin//e00cf25ad42683b3df678c61f42c6bda == admin1//0e750f2372908783b76cd1f1aa62b0b6 == nihao888System.out.println("string = " + string);}
}
使用工具类加密即可!