Shiro-学习总结-认证之自定义realm
2017-02-22 22:37
381 查看
用户自定义认证需要实现Realm,其实现类如下图所示:
推荐去继续org.apache.shiro.realm.AuthorizingRealm
创建MyRealm继承AuthorizingRealm
创建shiroRealm.ini配置文件:
创建测试程序:
密码认证过程中没有加入盐值,只是简单的认证。认证流程大体如下:
进入org.apache.shiro.authc.pam.ModularRealmAuthenticator类的下面方法:
然后调用org.apache.shiro.realm.AuthenticatingRealm类的下面方法:
之后调用本类中的assertCredentialsMatch方法:
密码比对会调用org.apache.shiro.authc.credential.SimpleCredentialsMatcher类的如下方法:
上面的举例中,密码比对为明文。开发中肯定不会这样使用。一般数据源中存储的都是加密后的密码及盐值,下面我们对上面的例子进行更改,以md5为例:
需要修改ini配置文件,及realm
md5Shiro.ini配置文件:
Md5Realm代码如下:
提供小程序用于生成数据库中加密后的密码:
推荐去继续org.apache.shiro.realm.AuthorizingRealm
public class ShiroRealm extends AuthorizingRealm{//AuthenticatingRealm {//implements Realm { @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 获取用户用户身份验证信息 return null; } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // 获取用户身份获取授权信息 return null; } }
简单举例:
pom.xml文件配置:<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.12</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> </dependency> </dependencies>
创建MyRealm继承AuthorizingRealm
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //从token中取出用户身份信息 String userName = (String) token.getPrincipal(); //根据userName去数据库中查询 //.... //模拟从数据库中查询的密码 String password = "123456"; //查询不到返回Null //查询到返回认证信息AuthorizationInfo SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName, password, getName()); return simpleAuthenticationInfo; }
创建shiroRealm.ini配置文件:
[main] myRealm = Lee.realm.MyRealm securityManager.realms = $myRealm
创建测试程序:
public class Quickstart { private static final Logger log = LoggerFactory.getLogger(Quickstart.class); public static void main(String[] args) { //构造SecurityManager //Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiroRealm.ini"); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); //获取当前subject Subject currentUser = SecurityUtils.getSubject(); //测试当前用户是否已经登录即执行认证 if (!currentUser.isAuthenticated()) { //用户名、密码封装为UsernamePasswordToken UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "123456"); //记住我 token.setRememberMe(true); try { //登录即执行认证 currentUser.login(token); } catch (UnknownAccountException uae) {//没有此账户 log.info("There is no user with username of " + token.getPrincipal()); System.out.println("==================="+uae+"==================="); } catch (IncorrectCredentialsException ice) {//密码错误 log.info("Password for account " + token.getPrincipal() + " was incorrect!"); System.out.println("==================="+ice+"==================="); } catch (LockedAccountException lae) {//账户被锁定 log.info("The account for username " + token.getPrincipal() + " is locked. " + "Please contact your administrator to unlock it."); System.out.println("==================="+lae+"==================="); }catch (AuthenticationException ae) { ae.printStackTrace(); } } System.out.println("是否认证通过"+currentUser.isAuthenticated()); //退出登录 currentUser.logout(); System.exit(0); } }
密码认证过程中没有加入盐值,只是简单的认证。认证流程大体如下:
进入org.apache.shiro.authc.pam.ModularRealmAuthenticator类的下面方法:
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException { assertRealmsConfigured(); Collection<Realm> realms = getRealms(); if (realms.size() == 1) { return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);//单个realm } else { return doMultiRealmAuthentication(realms, authenticationToken);//多个realm } }
然后调用org.apache.shiro.realm.AuthenticatingRealm类的下面方法:
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { AuthenticationInfo info = getCachedAuthenticationInfo(token); if (info == null) { //otherwise not cached, perform the lookup: info = doGetAuthenticationInfo(token);//获取用户身份信息,该方法由我们自定义的MyRealm覆盖 log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info); if (token != null && info != null) { cacheAuthenticationInfoIfPossible(token, info); } } else { log.debug("Using cached authentication info [{}] to perform credentials matching.", info); } if (info != null) { assertCredentialsMatch(token, info);//获取用户身份信息后,进行密码的比对。token为用户输入的信息,info为我们从数据源中查询的信息 } else { log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token); } return info; }
之后调用本类中的assertCredentialsMatch方法:
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException { CredentialsMatcher cm = getCredentialsMatcher();//默认为SimpleCredentialsMatcher 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."); } }
密码比对会调用org.apache.shiro.authc.credential.SimpleCredentialsMatcher类的如下方法:
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { Object tokenCredentials = getCredentials(token); Object accountCredentials = getCredentials(info); return equals(tokenCredentials, accountCredentials);//进行简单的equals比较 }
上面的举例中,密码比对为明文。开发中肯定不会这样使用。一般数据源中存储的都是加密后的密码及盐值,下面我们对上面的例子进行更改,以md5为例:
需要修改ini配置文件,及realm
md5Shiro.ini配置文件:
[main]
#定义凭证匹配器 credentialsMatcher=org.apache.shiro.authc.credential.Md5CredentialsMatcher
#设置散列算法 credentialsMatcher.hashAlgorithmName=MD5
#散列次数 credentialsMatcher.hashIterations=1024
#是否加盐 credentialsMatcher.hashSalted=true #将凭证匹配器设置到realm中 md5Realm = Lee.realm.Md5Realm md5Realm.credentialsMatcher=$credentialsMatcher securityManager.realms = $md5Realm
Md5Realm代码如下:
public class Md5Realm extends AuthorizingRealm{ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //根据用户输入的账户模拟查询数据库 //数据库中保存的用户密码 String dbaPassword="b8d63fc060e2b5651e8cb4e71ba61e6f"; //数据库中保存的盐值 ByteSource credentialsSalt=ByteSource.Util.bytes("aaa"); //用户输入的登录密码 Object principals=token.getPrincipal(); SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(principals, dbaPassword, credentialsSalt,getName()); return authenticationInfo; } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // TODO Auto-generated method stub return null; }
提供小程序用于生成数据库中加密后的密码:
public static void main(String[] args) { //加密方式 String hashAlgorithmName = "MD5"; //用户输入的密码 Object credentials = "123456"; //盐值 ByteSource credentialsSalt=ByteSource.Util.bytes("aaa"); //参数一:散列算法 参数二:原始密码 参数三:盐值 参数四:散列次数 System.out.println(new SimpleHash(hashAlgorithmName, credentials, credentialsSalt, 1024)); //参数一:原始密码 参数二:盐值 参数三:散列次数 Md5Hash md5Hash = new Md5Hash("123456", "aaa", 1024); System.out.println(md5Hash.toString()); }
相关文章推荐
- Shiro学习总结(3)——Apache Shiro身份认证
- shiro简介,认证,认证流程,自定义realm,散列算法
- Shiro Review——自定义Realm实现认证
- shiro使用总结-自定义Realm
- Shiro第二篇【介绍Shiro、认证流程、自定义realm、自定义realm支持md5】
- java安全框架-Shiro学习笔记(七)-自定义realm
- 学习Shiro——采用jdbcRealm实现身份认证
- 当shiro不进入自定义realm的权限认证方法时
- Shiro入门6:自定义realm查询数据库进行认证
- Shiro 学习笔记(3)—— 自定义 Realm
- Shiro-学习总结-认证
- Shiro 自定义realm授权与认证的实现
- java鬼混笔记:shiro 2、自定义realm进行认证
- 当shiro不进入自定义realm的权限认证方法时
- shiro授权,自定义realm实现授权,shiro与项目集成,在项目中实现认证及授权
- Shiro Review——自定义Realm实现认证
- Shiro学习总结(3)——Apache Shiro身份认证
- Shiro第二篇【介绍Shiro、认证流程、自定义realm、自定义realm支持md5】
- 【shiro】shiro学习笔记2-自定义realm
- Shiro第二篇【介绍Shiro、认证流程、自定义realm、自定义realm支持md5】