02月14, 2021

11、SpringBoot 与 Shiro 整合 (三)

SpringBoot与Shiro整合 (三)

十一、密码加密

1、概念

1.1、基本概念

对原有的内容进行编码得到不同于原始内容但是能够表示原有内容的数据。

在数据存储密码的时候如果不进行加密直接存储原文,如果数据库的信息泄露后就会造成用户信息的泄露。通过一定的规则将密码转换为密文,即使数据库中的数据泄露也不会造成用户信息的泄露

注册的时候对密码进行加密,登录的时候也需要进行加密,加密后用加密后的密码是不能登录的,只要加密规则不泄露就能保证用户信息的安全

1.2、加密规则

加密规则可以自定义,在项目开发中通常使用BASE64和MD5编码方式:

BASE64:可以反编码的编码方式,明文可以变为密文、密文可以变为明文

MD5:不可逆的编码方式,只能明文变密文,密码丢失只能重置不能获取原密码(非对称)

1.3、加盐

随机生成一个字符串与明文进行拼接,用拼接后的字符串进行加密,可以防止暴力破解的方式通过密文获取到明文

再加盐后破解到的明文含有添加的部分,并不是真实的用户的密码

二、快速开始

首先对原有数据库t_user表做出简单修改,加入盐值字段 -w553

-w953 密码存储的是MD5加盐值并且散列1024次之后的加密处理,salt为随机盐值

1、thymeleaf页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form th:action="@{/user/add}" method="post">
    注册名: <input type="text" name="username">
    <br>
    密 码: <input type="password" name="password"> 
    <br>
    昵 称: <input type="text" name="nickname">
    <br>
    <input type="submit" value="添加">
</form>
</body>
</html>

2、自定义加盐加密工具类

ShiroPasswordSaltUtil

package com.yingside.springbootdemo.util;

import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
import java.util.UUID;

public class ShiroPasswordSaltUtil {
    //加密方式
    public static final String HASH_ALGORITHM_NAME="MD5";

    /**
     * 生成32的随机盐值
     */
    public static String createSalt(){
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    public static final String md5Salt(String password){
        return md5Salt(password,createSalt());
    }

    public static final String md5Salt(Object password, String salt){
        //盐:为了即使相同的密码不同的盐加密后的结果也不同
        ByteSource byteSalt = ByteSource.Util.bytes(salt);
        System.out.println(salt);
        //加密次数
        int hashIterations = 1024;
        SimpleHash result = new SimpleHash(HASH_ALGORITHM_NAME, password, byteSalt, hashIterations);
        return result.toString();
    }
}

3、Controller类处理

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    private IUserService userService;

    //跳转thymeleaf页面
    @RequestMapping("/toAdd")
    public String form(@ModelAttribute("user") User user){
        return "register";
    }

    //添加用户
    @RequestMapping("/add")
    @RequiresPermissions("user:add")
    public String add(User user){
        //创建 盐
        String salt = ShiroPasswordSaltUtil.createSalt();
        user.setSalt(salt);
        //设置加盐加密后的密码
user.setPassword(ShiroPasswordSaltUtil.md5Salt(user.getPassword(),salt));

        boolean flag = userService.save(user);

        if(flag){
            return "index";
        }
        return "login";
    }
    ......
}

4、配置加密匹配HashedCredentialsMatcher

在ShiroConfig中需要加入HashedCredentialsMatcher加密匹配

@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
    HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
    // 使用md5 算法进行加密
    hashedCredentialsMatcher.setHashAlgorithmName("md5");
    // 设置散列次数: 意为加密几次
    hashedCredentialsMatcher.setHashIterations(1024);
    return hashedCredentialsMatcher;
}

5、自定义Realm中定义加密匹配

修改原来在ShiroConfig中获取UserRealm的设置,加入加密匹配HashedCredentialsMatcher

@Bean("userRealm")
public UserRealm getUserRealm(HashedCredentialsMatcher credentialsMatcher){
    UserRealm userRealm = new UserRealm();
    userRealm.setCredentialsMatcher(credentialsMatcher);
    return userRealm;
}

6、UserRealm认证逻辑中加入密码认证加入盐

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    System.out.println("==========执行认证逻辑==========");

    //1、判断用户名,token中的用户信息时登录的时候传进来的
    UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;

    User user = userService.findUserByUsername(token.getUsername());

    //盐值
    ByteSource salt = ByteSource.Util.bytes(user.getSalt());
    //String saltPassword = ShiroPasswordSaltUtil.md5Salt(token.getPassword(),salt.toString());

    if(user == null){
        return null;//返回null,shiro底层会抛出UnknownAccountException,证明没有这个用户名
    }

    SimpleAuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user, user.getPassword(), salt, this.getName());

    //清缓存中的授权信息,保证每次登陆 都可以重新授权。因为AuthorizingRealm会先检查缓存有没有 授权信息,再调用授权方法
    super.clearCachedAuthorizationInfo(authcInfo.getPrincipals());
    //从Subject中获取到Session把对象保存到session中
    SecurityUtils.getSubject().getSession().setAttribute("user",user);

    return authcInfo;
}

本文链接:http://www.yanhongzhi.com/post/springboot-11.html

-- EOF --

Comments