Java学习Day53:铲除紫云山金丹原料厂厂长(手机快速登录、权限控制)

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 的一些主要作用:

  1. 会话管理:Cookie 可以存储用户的登录状态,方便用户在网站上保持登录,免去每次访问时都要输入用户名和密码的麻烦。

  2. 个性化设置:网站可以通过 Cookie 保存用户的偏好设置,比如语言、主题、布局等,提供更个性化的浏览体验。

  3. 追踪和分析:Cookie 可以用于收集用户的浏览行为数据,帮助网站分析流量、优化内容和改进用户体验。

  4. 广告投放:许多网站使用 Cookie 来记录用户的兴趣和行为,以便为其提供更相关的广告内容。

  5. 购物车功能:在电商网站上,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);
    }
}

使用工具类加密即可!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/902233.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

centos7.x安装openCV 4.6.0版本

## 从源代码编译安装 1.更新系统 sudo yum update -y 2.安装依赖项 sudo yum groupinstall "Development Tools" sudo yum install cmake gcc-c git libjpeg-turbo-devel libpng-devel libtiff-devel libwebp-devel openexr-devel gstreamer1-plugins-base-devel…

iTerm2 保持SSH远程连接

1、保持SSH远程连接的稳定&#xff0c;防止因闲置时间过长而断开连接 When idle, send ASCII code 35 every 60 seconds每60秒 输入# 2、客户端设置保持活动 设置客户端每隔60秒发送一次保活信号&#xff0c;总共尝试3次。 vim ~/.ssh/configHost *ServerAliveInterval 60…

uniapp 底部导航栏tabBar设置后不显示的问题——已解决

uniapp 底部导航栏tabBar设置后不显示的问题——已解决 网上找了一堆解决办法&#xff0c;挨个对着试吧 解决办法一&#xff1a;tabBar里的list第一项和page中的第一项要相同&#xff0c;确实就能显示了。但是问题来了&#xff0c;page中的第一项是入口页&#xff0c;那就意味…

鲸鱼优化算法(Whale Optimization Algorithm, WOA)原理与MATLAB例程

鲸鱼优化算法&#xff08;Whale Optimization Algorithm, WOA&#xff09;是一种基于鲸鱼捕食行为的智能优化算法。它模拟了座头鲸在狩猎时的“气泡网”捕食策略。 文章目录 1.适应度函数2. 更新公式2.1 突袭行为2.2 螺旋更新3.线性递减参数4. 边界处理 MATLAB 实现示例代码说明…

HarmonyOS 5.0应用开发——Navigation实现页面路由

【高心星出品】 文章目录 Navigation实现页面路由完整的Navigation入口页面子页面 页面跳转路由拦截其他的 Navigation实现页面路由 Navigation&#xff1a;路由导航的根视图容器&#xff0c;一般作为页面&#xff08;Entry&#xff09;的根容器去使用&#xff0c;包括单页面&…

前端构建工具vite的优势

1. 极速冷启动 Vite 使用原生 ES 模块 (ESM) 在开发环境下进行工作。相比于传统构建工具需要打包所有的文件&#xff0c;Vite 只在浏览器请求模块时动态加载所需的文件。无打包冷启动&#xff1a;无需预先打包&#xff0c;项目启动非常快&#xff0c;尤其对于大型项目效果更明…

Arduino Uno 同时控制多路舵机

Arduino Uno同时控制4个舵机 舵机可以在0~180度内指定角度的控制。常用于航模、机器人、遥控玩具等物品,然而,很多时候要一次性控制多个舵机,今天以控制4个舵机为例进行说明 接线方式如下图: 舵机的信号线分别接A0,A1,A2,A3。控制舵机从0旋转到180度,再由180度旋转到0度,…

基于NERF技术重建学习笔记

NeRF&#xff08;Neural Radiance Fields&#xff09;是一种用于3D场景重建的神经网络模型&#xff0c;能够从2D图像生成逼真的3D渲染效果。它将场景表征为一个连续的5D函数&#xff0c;利用了体积渲染和神经网络的结合&#xff0c;通过学习光线穿过空间时的颜色和密度来重建场…

机器视觉-相机、镜头、光源(总结)

目录 1、机器视觉光源概述 2、光源的作用 3、光谱 4、工业场景常见光源 4.1、白炽灯 4.2、卤素灯 4.3、 荧光灯 4.4、LED灯 4.5、激光灯 5、光源的基本性能 5.1、光通量 5.2、光效率 5.3、发光强度 5.4、光照度 5.5、均匀性 5.6、色温 5.7、显色性 6、基本光学…

openpnp - 解决“底部相机高级校正成功后, 开机归零时,吸嘴自动校验失败的问题“

文章目录 openpnp - 解决"底部相机高级校正成功后, 开机归零时&#xff0c;吸嘴自动校验失败的问题"概述笔记问题现象1问题现象2原因分析现在底部相机和吸嘴的位置偏差记录修正底部相机位置现在再看看NT1在底部相机中的位置开机归零&#xff0c;看看是否能通过所有校…

python csv库

python csv库 水一水又是一篇&#xff0c;乐 读取 import csv # 打开 CSV 文件 with open(example.csv, moder, newline) as file: csv_reader csv.reader(file) # 读取文件头&#xff08;可选&#xff09; headers next(csv_reader) print(f"Headers: {heade…

golang将指针传给cgo后还能被回收吗?

问题&#xff1a; 如果把golang分配的变量&#xff0c;其指针通过cgo传给c&#xff0c;并被c存储&#xff0c;那这个变量还能被gc回收吗&#xff1f; 实验代码&#xff1a; test_memory_leak.go package main/* #include <stdlib.h> #include <string.h> #incl…

基于docker-compose编排部署微服务快速开发框架

1. 规划节点 节点规划&#xff0c;见表1。 表1 节点规划 IP主机名节点10.24.2.10masterdocker-compose节点 2. 基础准备 Docker和Docker Compose已安装完成&#xff0c;将提供的软件包Pig.tar.gz上传至master节点/root目录下并解压。 案例实施 1. 基础环境准备 &#x…

渗透测试-百日筑基—SQL注入篇时间注入绕过HTTP数据编码绕过—下

day8-渗透测试sql注入篇&时间注入&绕过&HTTP数据编码绕过 一、时间注入 SQL注入时间注入&#xff08;也称为延时注入&#xff09;是SQL注入攻击的一种特殊形式&#xff0c;它属于盲注&#xff08;Blind SQL Injection&#xff09;的一种。在盲注中&#xff0c;攻击…

模型评估:Accuracy、Precision、Recall、F1、ROC曲线、AUC、PR曲线

Accuracy & Precision & Recall & F1 准确率 Accuracy A c c u r a c y T T T F A L L Accuracy \frac{TT TF}{ALL} AccuracyALLTTTF​ 1.分类器到底分对了多少&#xff1f; 精确率 Precision 2.返回的图片中正确的有多少&#xff1f; 召回率 / 查全率 …

了解光耦合器输入输出关系---腾恩科技

光耦合器&#xff0c;也称为光隔离器&#xff0c;是电子电路中必不可少的元件&#xff0c;主要用于在隔离部分之间传输信号&#xff0c;同时防止电噪声或高压影响敏感元件。其独特的设计使它们能够在没有直接电接触的情况下&#xff0c;弥合不同电压域之间的差距。在本文中&…

解决docker拉取readeck镜像报Error response from daemon: toomanyrequests问题

readeck 是一个内容中心&#xff0c;目前已支持中文翻译 这是本地化部署后的效果&#xff1a; 原命令为&#xff1a; docker run --rm -ti -p 8000:8000 -v readeck-data:/readeck codeberg.org/readeck/readeck:latest Unable to find image codeberg.org/readeck/readeck:la…

LeetCode 热题 100之普通数组

1.最大子数组和 思路分析&#xff1a;这个问题可以通过动态规划来解决&#xff0c;我们可以使用Kadane’s Algorithm&#xff08;卡登算法&#xff09;来找到具有最大和的连续子数组。 Kadane’s Algorithm 的核心思想是利用一个变量存储当前的累加和 currentSum&#xff0c;并…

【高中生讲机器学习】22. 信息论基础:信息熵、交叉熵、相对熵

创建时间&#xff1a;2024-10-16 首发时间&#xff1a;2024-10-24 最后编辑时间&#xff1a;2024-10-24 作者&#xff1a;Geeker_LStar FIRST OF ALL!!! 2024.10.24&#xff01;&#xff01; 1024 快乐&#xff01;&#xff01;&#xff01; 你好呀~这里是 Geeker_LStar 的人工…

IDEA初探:深入理解 Structure 功能

一、Structure - 类视图 Structure 是 IDEA 中的一个视图工具&#xff0c;它提供了对当前文件中结构元素的快速访问。通过 Structure&#xff0c;我们可以方便地查看和导航到代码中的各个部分&#xff0c;从而提高代码编辑和浏览的效率。 1.1 基本概念 Structure 视图以树形结…