shiro-密码比较的设计 CredentialsMatcher -为什么Java中的密码优先使用 char[] 而不是String?

本文主要是介绍shiro-密码比较的设计 CredentialsMatcher -为什么Java中的密码优先使用 char[] 而不是String?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

密码比较
昨天的时候,笔者仔细的追踪了,整个获取信息的主要的设计,通过用户的唯一标识得到 AuthenticationInfo 然后和 AuthenticationToken (用户名 密码),进行比较! 有一个专门的设计类,用来处理密码匹配的比较的。而且很复杂~

AuthenticatingRealm中有一个成员变量
private CredentialsMatcher credentialsMatcher; 凭据的匹配,就是密码的比较辣。这个是一个接口!默认的实现为 SimpleCredentialsMatcher 这个类!都是面向接口编程的~

AuthenticatingRealm 类中的一个函数,通过匹配的实现,进行密码信息的比较工作。

 protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {CredentialsMatcher cm = getCredentialsMatcher();if (cm != null) {//if (!cm.doCredentialsMatch(token, info)) {//not successful - throw an exception to indicate this:String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";throw new IncorrectCredentialsException(msg);}} else {throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +"credentials during authentication.  If you do not wish for credentials to be examined, you " +"can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");}}

看看比较类信息的继承图 可以看出来这里的SimpleCredentialsMatcher 只是简单的实现加密,下面的Hash的单向加密算法MD5,SHA子类的算法 http://www.cnblogs.com/crazylqy/p/4813483.html 对此不是非常的了解,一般的JDK都是要实现的。接下来就是看看这个密码匹配的整个炉子实现的解析!,你发现没有,无论是realm门面还是我们的密码匹配都是设计的扩展性十足的,这样的学习列子,学习看源码好处真的非常棒!
这里写图片描述

CredentialsMatcher 接口,这个接口呢,结合了昨天的,一个是我们登录的时候密码和用户名,AuthenticationToken的实现类 和 通过用户名获得的当前用户的所有的信息,昨天已经很详细的解析了,比如权限,角色信息等等。AuthenticationInfo的实现类 SimpleAuthenticationInfo 。这两个信息的凭证也是密码进行比较,就是实现的意义。

/*** Interface implemented by classes that can determine if an AuthenticationToken's provided* credentials matches a corresponding account's credentials stored in the system.** <p>Simple direct comparisons are handled well by the* {@link SimpleCredentialsMatcher SimpleCredentialsMatcher}.  If you* hash user's credentials before storing them in a realm (a common practice), look at the* {@link HashedCredentialsMatcher HashedCredentialsMatcher} implementations,* as they support this scenario.** @see SimpleCredentialsMatcher* @see AllowAllCredentialsMatcher* @see Md5CredentialsMatcher* @see Sha1CredentialsMatcher* @since 0.1*/
public interface CredentialsMatcher {/*** Returns {@code true} if the provided token credentials match the stored account credentials*/boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info);}

CodecSupport这个抽象类,是实现了一个Utis方法的类,充满了String,Char Byte之间的转换。


/*** Base abstract class that provides useful encoding and decoding operations, especially for character data.** @since 0.9*/
public abstract class CodecSupport {public static final String PREFERRED_ENCODING = "UTF-8";/*** @param chars the character array to be converted to a byte array.* @return the byte array of the UTF-8 encoded character array.*/public static byte[] toBytes(char[] chars) {return toBytes(new String(chars), PREFERRED_ENCODING);}public static byte[] toBytes(char[] chars, String encoding) throws CodecException {return toBytes(new String(chars), encoding);}public static byte[] toBytes(String source) {return toBytes(source, PREFERRED_ENCODING);}/*** Converts the specified source to a byte array via the specified encoding, throwing a* {@link CodecException CodecException} if the encoding fails.*/public static byte[] toBytes(String source, String encoding) throws CodecException {try {return source.getBytes(encoding);} catch (UnsupportedEncodingException e) {String msg = "Unable to convert source [" + source + "] to byte array using " +"encoding '" + encoding + "'";throw new CodecException(msg, e);}}/*** Converts the specified byte array to a String using * 将bytes转换为String */public static String toString(byte[] bytes) {return toString(bytes, PREFERRED_ENCODING);}/*** Converts the specified byte array to a String using the specified character encoding.  This implementation* does the same thing as <code>new {@link String#String(byte[], String) String(byte[], encoding)}</code>, but will* wrap any {@link UnsupportedEncodingException} with a nicer runtime {@link CodecException}, allowing you to* decide whether or not you want to catch the exception or let it propagate.*/public static String toString(byte[] bytes, String encoding) throws CodecException {try {return new String(bytes, encoding);} catch (UnsupportedEncodingException e) {String msg = "Unable to convert byte array to String with encoding '" + encoding + "'.";throw new CodecException(msg, e);}}/*** Returns the specified byte array as a character array using the* bytes ->String->Char* {@link CodecSupport#PREFERRED_ENCODING PREFERRED_ENCODING}.*/public static char[] toChars(byte[] bytes) {return toChars(bytes, PREFERRED_ENCODING);}public static char[] toChars(byte[] bytes, String encoding) throws CodecException {return toString(bytes, encoding).toCharArray();}protected boolean isByteSource(Object o) {return o instanceof byte[] || o instanceof char[] || o instanceof String ||o instanceof ByteSource || o instanceof File || o instanceof InputStream;}protected byte[] toBytes(Object o) {if (o == null) {String msg = "Argument for byte conversion cannot be null.";throw new IllegalArgumentException(msg);}if (o instanceof byte[]) {return (byte[]) o;} else if (o instanceof ByteSource) {return ((ByteSource) o).getBytes();} else if (o instanceof char[]) {return toBytes((char[]) o);} else if (o instanceof String) {return toBytes((String) o);} else if (o instanceof File) {return toBytes((File) o);} else if (o instanceof InputStream) {return toBytes((InputStream) o);} else {return objectToBytes(o);}}protected String toString(Object o) {if (o == null) {String msg = "Argument for String conversion cannot be null.";throw new IllegalArgumentException(msg);}if (o instanceof byte[]) {return toString((byte[]) o);} else if (o instanceof char[]) {return new String((char[]) o);} else if (o instanceof String) {return (String) o;} else {return objectToString(o);}}protected byte[] toBytes(File file) {if (file == null) {throw new IllegalArgumentException("File argument cannot be null.");}try {return toBytes(new FileInputStream(file));} catch (FileNotFoundException e) {String msg = "Unable to acquire InputStream for file [" + file + "]";throw new CodecException(msg, e);}}protected byte[] toBytes(InputStream in) {if (in == null) {throw new IllegalArgumentException("InputStream argument cannot be null.");}final int BUFFER_SIZE = 512;ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE);byte[] buffer = new byte[BUFFER_SIZE];int bytesRead;try {while ((bytesRead = in.read(buffer)) != -1) {out.write(buffer, 0, bytesRead);}return out.toByteArray();} catch (IOException ioe) {throw new CodecException(ioe);} finally {try {in.close();} catch (IOException ignored) {}try {out.close();} catch (IOException ignored) {}}}protected byte[] objectToBytes(Object o) {String msg = "The " + getClass().getName();throw new CodecException(msg);}protected String objectToString(Object o) {return o.toString();}
}

SimpleCredentialsMatcher 只是简单的进行了扩展,没有使用到加密MD5子类的信息~


public class SimpleCredentialsMatcher extends CodecSupport implements CredentialsMatcher {protected Object getCredentials(AuthenticationToken token) {return token.getCredentials();}protected Object getCredentials(AuthenticationInfo info) {return info.getCredentials();}protected boolean equals(Object tokenCredentials, Object accountCredentials) {if (log.isDebugEnabled()) {log.debug("Performing credentials equality check for tokenCredentials of type [" +tokenCredentials.getClass().getName() + " and accountCredentials of type [" +accountCredentials.getClass().getName() + "]");}if (isByteSource(tokenCredentials) && isByteSource(accountCredentials)) {if (log.isDebugEnabled()) {log.debug("Both credentials arguments can be easily converted to byte arrays.  Performing " +"array equals comparison");}byte[] tokenBytes = toBytes(tokenCredentials);byte[] accountBytes = toBytes(accountCredentials);return Arrays.equals(tokenBytes, accountBytes);} else {return accountCredentials.equals(tokenCredentials);}}/*** 判断一下是否相等,得到凭证!这里使用Object的意思也是为了各种可能的扩展吧**/public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {Object tokenCredentials = getCredentials(token);Object accountCredentials = getCredentials(info);return equals(tokenCredentials, accountCredentials);}}

下面的Hash的处理,不太想去看了,各种版本,有很多废弃啦,写的很烦~~~,不过知道意思就好了。

为什么Java中的密码优先使用 char[] 而不是String?
https://www.zhihu.com/question/36734157 知乎答的说的不错!但是最好还是加密
String在Java中是不可变对象,如果作为普通文本存储密码,那么它会一直存在内存中直至被垃圾收集器回收。这就意味着一旦创建了一个字符串,如果另一个进程把尝试内存的数据导出(dump),在GC进行垃圾回收之前该字符串会一直保留在内存中,那么该进程就可以轻易的读取到该字符串。

而对于数组,可以在使用该数组之后显示地擦掉数组中的内容,你可以使用其他不相关的内容把数组内容覆盖掉,例如,在使用完密码后,我们将char[]的值均赋为0,如果有人能以某种方式看到内存映像,他只能看到一串0;而如果我们使用的是字符串,他们便能以纯文本方式看到密码。因此,使用char[]是相对安全的。

推荐使用char[],这是从安全角度来选择的。但是,我们应当注意到,即使是用char[]处理密码也只是降低被攻击的概率而已,还是会有其他方法攻破数组处理的密码。

另一方面,使用String的时候,你可能会不经意间将密码打印出来(如log文件),此时,使用char[]就显得更加的安全了,如:

public static void main(String[] args) {
Object pw = “Password”;
System.out.println(“String: ” + pw);

pw = "Password".toCharArray();
System.out.println("Array: " + pw);

}

此时的输出结果将会是

String: PasswordArray: [C@5829428e

实际上,即使使用了char[]保存密码也仍然不够安全,内存中还是可能会有这串数据的零碎副本,因此,建议使用加密的密码来代替普通的文本字符串密码,并且在使用完后记得立即清除。

这篇关于shiro-密码比较的设计 CredentialsMatcher -为什么Java中的密码优先使用 char[] 而不是String?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1091612

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

百度/小米/滴滴/京东,中台架构比较

小米中台建设实践 01 小米的三大中台建设:业务+数据+技术 业务中台--从业务说起 在中台建设中,需要规范化的服务接口、一致整合化的数据、容器化的技术组件以及弹性的基础设施。并结合业务情况,判定是否真的需要中台。 小米参考了业界优秀的案例包括移动中台、数据中台、业务中台、技术中台等,再结合其业务发展历程及业务现状,整理了中台架构的核心方法论,一是企业如何共享服务,二是如何为业务提供便利。

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取