0
点赞
收藏
分享

微信扫一扫

Spring基于Shiro整合Bcrypt加密方式验证

Star英 2022-02-25 阅读 72

本文记录基于已有的SSM+Shiro框架项目,将密码验证方式改为BCrypt随机盐加密算法。


众所皆知,MD5加密方式虽然不可逆但并不安全,BCrypt加密算法比MD5安全性更高,因此很多项目的加密方式也需要做一个改变和升级,关于BCrypt加密算法更多具体的原理可以参考此文:https://blog.csdn.net/m0_37609579/article/details/100785947


shiro最常见的密码验证方式,也是他提供默认的加密方式一般为MD5以及MD5加盐的处理,因此需要自己手动配置做一些调整达成BCrypt随机盐加密。


一、pom.xml中引入jBCrypt的依赖

		<!-- jBCrypt -->
        <dependency>
            <groupId>de.svenkubiak</groupId>
            <artifactId>jBCrypt</artifactId>
            <version>0.4.1</version>
        </dependency>

二、创建Bcrypt加密工具类


import org.mindrot.jbcrypt.BCrypt;

/**
 * @ClassName: BcryptUtil
 * @Author: XIZI
 * @Description: Bcrypt随机盐加密
 */
public class BcryptUtil {
    /**
     * 对明文密码进行加密,并返回加密后的密码
     *
     * @param password 明文密码
     * @return
     */
    public static String encode(String password) {
        return BCrypt.hashpw(password, BCrypt.gensalt());
    }

    /**
     * 将明文密码跟加密后的密码进行匹配,如果一致返回true,否则返回false
     *
     * @param password       明文密码
     * @param encodePassword 加密后的密码
     * @return
     */
    public static boolean match(String password, String encodePassword) {
        return BCrypt.checkpw(password, encodePassword);
    }


}

三、创建一个自定义密码凭证


import com.xizi.util.BcryptUtil;
import org.apache.log4j.Logger;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;

/**
 * @ClassName: CustomCredentialsMatcher
 * @Author: XIZI
 * @Description: 自定义密码凭证
 */
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        String password = new String(userToken.getPassword());
        //这是数据库里查出来的(加密后的)密码
        String encodePassword = info.getCredentials().toString();
        //进行比对
        return BcryptUtil.match(password, encodePassword);
    }
}

四、修改shiro的配置文件 spring-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/userLogin/toLogin"/>
        <property name="unauthorizedUrl" value="/userLogin/toNoroot"/><!-- 没有权限跳转的地址 -->
        <property name="filterChainDefinitions">
            <value>
            </value>
        </property>
    </bean>

    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="MyRealm"/>
    </bean>

	<!--主要修改下面这两块配置 -->
    <!-- 自定义的Realm -->
    <bean id="MyRealm" class="com.xizi.shiro.MyRealm">
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>
    <!-- 自定义密码凭证匹配器 -->
    <bean id="credentialsMatcher" class="com.xizi.shiro.CustomCredentialsMatcher"/>


</beans>

五、实现shiro逻辑,将BCrypt加密方式融入

import org.apache.log4j.Logger;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @ClassName: MyRealm
 * @Author: XIZI
 * @Description: shiro逻辑
 */
public class MyRealm extends AuthorizingRealm {

    @Autowired
    SysUserDao sysUserDao;

    /**
     * 授权,在配有缓存的情况下,只加载一次
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //获取帐号信息
        String account  =   (String)principals.getPrimaryPrincipal();
		SysUser user = sysUserDao.select(account);
        if (null != user) {
            //相关信息赋值
            SimpleAuthorizationInfo info	=	new SimpleAuthorizationInfo();
			//此处省略相关逻辑处理
            //....
			
            return info;
        }
        return null;
    }

    /**
     * 认证登录 -- 查询数据库,如果该用户名正确,得到正确的数据,并返回正确的数据
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        //获取帐号 判断帐号是否存在
        String account = (String) authenticationToken.getPrincipal();
		SysUser user = sysUserDao.select(account);
		
        if (null != user) {
            //shiro校验
            String passWord = user.getPassword();
            String realName = user.getRealName();

			//此处密码验证,会调用我们配置Bcrypt加密的自定义密码凭证进行验证
            SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(account, passWord, null, realName);
            
            return authenticationInfo;
        }

        return null;
    }
}

六、service层的shiro逻辑

import com.xizi.constants.Constants;
import com.xizi.exception.ErrorException;
import org.apache.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;

/**
 * @ClassName: LoginService
 * @Author: XIZI
 * @Description: 登录验证逻辑
 */
@Service("LoginService")
public class LoginService {
    /**
     * 日志
     */
    private static final Logger logger = Logger.getLogger(LoginService.class);

    /**
     * SHIRO登陆
     *
     * @param account  帐号
     * @param password 明文密码
     * @param request  request
     */
    private void login(String account, String password, HttpServletRequest request) throws ErrorException {
        //shiro
        UsernamePasswordToken token = new UsernamePasswordToken(account, password);
        token.setRememberMe(false);
        try {
            Subject subject = SecurityUtils.getSubject();
            subject.login(token);
			
        } catch (UnknownAccountException e) {
            logger.error("账号不存在!");
			//向上抛出自定义报错
            throw new ErrorException(Constants.API_ERROR_FAIL, "账号或密码错误");
        } catch (IncorrectCredentialsException e) {
            logger.error("密码错误!");
			//向上抛出自定义报错
            throw new ErrorException(Constants.API_ERROR_FAIL, "账号或密码错误");
        }
    }

}


七、接口调用结果

自己本地生成一串加密后的字符串存储数据库中测试。
在这里插入图片描述

举报

相关推荐

0 条评论