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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/security
http://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"));
}
@Override
public 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位,需要前面补0
for (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 == nihao888
System.out.println("string = " + string);
}
}
使用工具类加密即可!