zoukankan      html  css  js  c++  java
  • Shiro 之 HashedCredentialsMatcher 认证匹配

    前言

    Shiro 提供了用于加密密码验证密码服务的 CredentialsMatcher 接口,而 HashedCredentialsMatcher 正是 CredentialsMatcher 的一个实现类。写项目的话,总归会用到用户密码的非对称加密,目前主流的非对称加密方式是 MD5 ,以及在 MD5 上的加盐处理,而 HashedCredentialsMatcher 也允许我们指定自己的算法和盐。本文将介绍 HashedCredentialsMatcher 的使用,以及对相关源码的进行解析,MD5 等加密知识请自行查阅。

    HashedCredentialsMatcher 的使用

    要使用 HashedCredentialsMatcher ,那么首先要进行配置。当然,前提是你已经引入了 Shiro 库。总共有三种配置方式:

    1. XML 格式

      <bean id="credentialsMatcher"
              class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        		<!-- 加密方式 -->
              <property name="hashAlgorithmName" value="MD5" />
        		<!-- 加密次数 -->
              <property name="hashIterations" value="2" />
        		<!-- 存储散列后的密码是否为16进制 -->
              <property name="storedCredentialsHexEncoded" value="true" />
      </bean>
      
    2. ini 等配置文件

      首先在 web.xml 中自定义 shiro.ini 位置

      <filter>
          <filter-name>ShiroFilter</filter-name>
          <filter-class>org.apache.shiro.web.servlet.IniShiroFilter</filter-class>
          <init-param>
              <param-name>configPath</param-name>
              <param-value>/WEB-INF/shiro.ini</param-value>
          </init-param>
      </filter>
      

      然后除了 shiro 的通常配置之外,需加上:

      credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
      ## 加密方式
      credentialsMatcher.hashAlgorithmName=md5
      ## 加密次数
      credentialsMatcher.hashIterations=2
      ## 存储散列后的密码是否为16进制 
      credentialsMatcher.storedCredentialsHexEncoded=true
      
    3. 建立 ShiroConfiguration 配置类,除了 shiro 的通常配置之外,需加上:

          @Bean
          public HashedCredentialsMatcher hashedCredentialsMatcher(){
              HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
            	//加密方式
              hashedCredentialsMatcher.setHashAlgorithmName("md5");
            	//加密次数
              hashedCredentialsMatcher.setHashIterations(2);
            	//存储散列后的密码是否为16进制
            	//hashedCredentialsMatcher.isStoredCredentialsHexEncoded();
              return hashedCredentialsMatcher;
          }
      

    然后,在登录方法或者自定义的输入中获取登录 token,我选择的方式是在/login中获取:

        public Object login(@RequestBody User userParam, HttpSession session) {
            String name =  userParam.getName();
            name = HtmlUtils.htmlEscape(name);
            Subject subject = SecurityUtils.getSubject();
          	// 生成token
            UsernamePasswordToken token = new UsernamePasswordToken(name, userParam.getPassword());
            try {
              	// 从自定义Realm获取安全数据进行验证
                subject.login(token);
                User user = userService.getByName(name);
                session.setAttribute("user", user);
                return Result.success();
            } catch (AuthenticationException e) {
                String message ="账号密码错误";
                return Result.fail(message);
            }
        }
    

    接下来,是自定义 Realm,之前我也有博文写过相关知识,所以只贴下代码作参考:

    public class JPARealm extends AuthorizingRealm {
    
    	@Autowired
    	private UserService userService;
    
    	@Override
    	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    		// 权限分配的相关知识在此不做介绍,重点在验证方面
    		SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
    		return s;
    	}
    
    	@Override
    	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    		String userName = token.getPrincipal().toString();
    		User user = userService.getByName(userName);
    		String passwordInDB = user.getPassword();
    		String salt = user.getSalt();
          	// 认证信息token里存放账号密码, getName() 是当前Realm的继承方法,通常返回当前类名
          	// 盐也放进去
            // 这样通过配置中的 HashedCredentialsMatcher 进行自动校验
    		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, passwordInDB, ByteSource.Util.bytes(salt),
    				getName());
    		return authenticationInfo;
    	}
    }
    

    HashedCredentialsMatcher 的源码分析

    从开发者的角度来看,我们可以自己实现 CredentialsMatcher 的一个类来实现定制化的账户密码验证机制,例如:

    public class MyCredentialsMatcher extends SimpleCredentialsMatcher {
        @Override
        public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
              Object tokenCredentials = getCredentials(token);
            Object accountCredentials = getCredentials(info);
            return super.equals(tokenCredentials, accountCredentials);
        }
    }
    

    SimpleCredentialsMatcher 是 CredentialsMatcher 这个类的默认实现,相信我,你基本上不会去自己实现 getCredentials 这种涉及到底层编码的方法,重点在于重写 doCredentialsMatch ,在这里你可以自定义账户密码验证机制。

    不过实现 doCredentialsMatch 你还是有可能觉得麻烦,HashedCredentialsMatcher 封装好了 doCredentialsMatch() 方法,你可以完全不用管它。

    上一节的使用中,流程就是:使用 token 类将用户输入的信息封装,然后采用 token 进行 login 操作。此时 shiro 将使用 token 中携带的用户信息调用 Realm 中自定义的 doGetAuthenticationInfo 方法进行校验比对,比对成功则登录成功。

    参考

    1. Shiro+SpringMVC 实现更安全的登录(加密匹配&登录失败超次数锁定帐号)—— 小灯光环
    2. shiro学习之HashedCredentialsMatcher密码匹配过程 —— MR.HE
    3. Shiro笔记(三)----Shiro配置文件ini详解 —— lfendo
    4. shiro-密码比较的设计 CredentialsMatcher -为什么Java中的密码优先使用 char[] 而不是String? —— 汪小哥
    5. 自定义shiro的Realm实现和CredentialsMatcher实现以及Token实现 —— 蓝萝卜blu
  • 相关阅读:
    Java 连oracle 12C 起步
    powershell excel 导入 sqlserver
    移动端适配方案(上)
    ie7兼容问题
    node学习第三天(2)
    node学习第三天(1)
    HTMl5的sessionStorage和localStorage的一些区别
    html5+css3实战之-幽灵按钮
    node.js理论知识梳理
    node.js学习第二天
  • 原文地址:https://www.cnblogs.com/Sherlock-J/p/12925945.html
Copyright © 2011-2022 走看看