当前位置: 首页 > article >正文

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);
    }
}

使用工具类加密即可!


http://www.kler.cn/news/367971.html

相关文章:

  • Vue 3 的组件式开发(3)
  • Pyspark中pyspark.sql.functions常用方法(4)
  • [LeetCode] 494. 目标和
  • C语言实现Go的defer功能
  • 2024高等代数【南昌大学】
  • sass软件登录设定——未来之窗行业应用跨平台架构
  • 浅谈AI大模型的数据特点和应用问题
  • JavaEE初阶---多线程(五)---定时器/线程池介绍
  • 如何在国内安装使用Python,国内镜像站点加速库的安装
  • 用哪种建站程序做谷歌SEO更容易?
  • P6458 [COCI2006-2007#5] LIGA
  • 算法汇总整理篇——贪心与动态规划学习及框架思考
  • ReactNative 启动应用(2)
  • 【Linux操作系统】Linux配置OpenSSH服务器步骤记录
  • 【Linux】操作系统初步理解与Linux指令入门
  • CesiumJS 案例 P6:添加图片图层、添加图片图层并覆盖指定区域
  • Kafka文档阅读笔记之基本操作
  • js构造函数和原型对象,ES6中的class,四种继承方式
  • FreeSWITCH 简单图形化界面30 - 使用MYODBC时可能遇到的错误
  • 宝塔-修改docker加速镜像-daemon.json配置文件格式错误!
  • android 与网页交互通过网页修改宿主布局和异常处理——未来之窗行业应用跨平台架构
  • 【OpenAI】第五节(图像生成)利用 OpenAI 的 DALL·E 实现自动化图像生成:从文本到图像的完整教程
  • 【报错解决】C++ 出现错误error: default argument given for parameter的解决办法
  • 15分钟学 Go 第 14 天:切片
  • 详细解读 CVPR2024:VideoBooth: Diffusion-based Video Generation with Image Prompts
  • Matlab 疲劳检测系统【源码 GUI】