转自:Shiro系列七:Realm

Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色和权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。如我们之前的ini配置方式将使用org.apache.shiro.realm.text.IniRealm。
org.apache.shiro.realm.Realm接口如下:

  1. String getName(); //返回一个唯一的Realm名字
  2. boolean supports(AuthenticationToken token); //判断此Realm是否支持此Token
  3. AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException; //根据Token获取认证信息

自定义 Realm

Shiro Realm 简介 - 图1

自定义 Realm 只需要继承AuthorizingRealm,就能够继承到认证与授权功能。AuthorizingRealm继承了 AuthenticatingRealm(即身份验证),而且也间接继承了 CachingRealm(带有缓存实现)。 其中主要默认实现如下:
 1、org.apache.shiro.realm.text.IniRealm:[users]部分指定用户名/密码及其角色;[roles]部分指定角色即权限信息;
 2、org.apache.shiro.realm.text.PropertiesRealm: user.username=password,role1,role2指定用户名/密码及其角色;role.role1=permission1,permission2指定角色及权限信息;
 3、org.apache.shiro.realm.jdbc.JdbcRealm:通过sql查询相应的信息,如“select password from users where username = ?”获取用户密码,“select password, password_salt from users where username = ?”获取用户密码及盐;“select role_name from user_roles where username = ?”获取用户角色;“select permission from roles_permissions where role_name = ?”获取角色对应的权限信息;也可以调用相应的api进行自定义sql;

  1. public class DefaultRealm extends AuthorizingRealm {
  2. //授权方法
  3. protected AuthorizationInfo doGetAuthorizationInfo(
  4. PrincipalCollection principalCollection) {
  5. return null;
  6. }
  7. //认证方法
  8. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
  9. throws AuthenticationException {
  10. return null;
  11. }
  12. }

多 Realm 的配置

1、再创建一个 AuthenticatingRealm 的子类 SecondRealm。

  1. public class SecondRealm extends AuthenticatingRealm {
  2. @Override
  3. protected AuthenticationInfo doGetAuthenticationInfo(
  4. AuthenticationToken token) throws AuthenticationException {
  5. System.out.println("[SecondReaml] doGetAuthenticationInfo");
  6. //1. 把 AuthenticationToken 转换为 UsernamePasswordToken
  7. UsernamePasswordToken upToken = (UsernamePasswordToken) token;
  8. //2. 从 UsernamePasswordToken 中来获取 username
  9. String username = upToken.getUsername();
  10. //3. 调用数据库的方法, 从数据库中查询 username 对应的用户记录
  11. System.out.println("从数据库中获取 username: " + username + " 所对应的用户信息.");
  12. //4. 若用户不存在, 则可以抛出 UnknownAccountException 异常
  13. if("unknown".equals(username)){
  14. throw new UnknownAccountException("用户不存在!");
  15. }
  16. //5. 根据用户信息的情况, 决定是否需要抛出其他的 AuthenticationException 异常.
  17. if("monster".equals(username)){
  18. throw new LockedAccountException("用户被锁定");
  19. }
  20. //6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo
  21. //以下信息是从数据库中获取的.
  22. // 1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象.
  23. Object principal = username;
  24. // 2). credentials: 密码.
  25. Object credentials = null;
  26. if("admin".equals(username)){
  27. credentials = "ce2f6417c7e1d32c1d81a797ee0b499f87c5de06";
  28. }else if("user".equals(username)){
  29. credentials = "073d4c3ae812935f23cb3f2a71943f49e082a718";
  30. }
  31. // 3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可
  32. String realmName = getName();
  33. // 4). 盐值.
  34. ByteSource credentialsSalt = ByteSource.Util.bytes(username);
  35. SimpleAuthenticationInfo info = new SimpleAuthenticationInfo("secondRealmName", credentials, credentialsSalt, realmName);
  36. return info;
  37. }
  38. public static void main(String[] args) {
  39. String hashAlgorithmName = "SHA1";
  40. Object credentials = "123456";
  41. Object salt = ByteSource.Util.bytes("admin");;
  42. int hashIterations = 1024;
  43. Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
  44. System.out.println(result);
  45. }
  46. }

2、在Spring配置文件中配置 ShiroRealm 和 SecondRealm 两个 Realm。

  1. <!--
  2. 将认证器 Authenticator 配置到 SecurityManager 中
  3. -->
  4. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
  5. <property name="cacheManager" ref="cacheManager"/>
  6. <property name="authenticator" ref="authenticator"></property>
  7. </bean>
  8. <!-- 配置认证器 Authenticator -->
  9. <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
  10. <property name="realms">
  11. <list>
  12. <ref bean="jdbcRealm"/>
  13. <ref bean="secondRealm"/>
  14. </list>
  15. </property>
  16. </bean>
  17. <!--
  18. 配置 Realm,直接配置实现了 org.apache.shiro.realm.Realm 接口的 bean
  19. -->
  20. <bean id="jdbcRealm" class="com.atguigu.shiro.realms.ShiroRealm">
  21. <property name="credentialsMatcher">
  22. <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
  23. <property name="hashAlgorithmName" value="MD5"></property>
  24. <property name="hashIterations" value="1024"></property>
  25. </bean>
  26. </property>
  27. </bean>
  28. <bean id="secondRealm" class="com.atguigu.shiro.realms.SecondRealm">
  29. <property name="credentialsMatcher">
  30. <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
  31. <property name="hashAlgorithmName" value="SHA1"></property>
  32. <property name="hashIterations" value="1024"></property>
  33. </bean>
  34. </property>
  35. </bean>

补充:
 在之前我们在配置SecurityManager时,把Realm作为一个属性直接配置在SecurityManager中。

  1. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
  2. <property name="cacheManager" ref="cacheManager"/>
  3. <property name="realm" ref="jdbcRealm"/>
  4. </bean>

那么在多Realm我们也可以直接将Realm配置到SecurityManager中,而不需要在Authenticator中配置。这样配置的原因是shiro在授权时需要使用 SecurityManager 的 realms 属性。

  1. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
  2. <property name="cacheManager" ref="cacheManager"/>
  3. <property name="authenticator" ref="authenticator"></property>
  4. <property name="realms">
  5. <list>
  6. <ref bean="jdbcRealm"/>
  7. <ref bean="secondRealm"/>
  8. </list>
  9. </property>
  10. </bean>
  11. <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator"></bean>

思考:既然没有在Authenticator中配置Realm,那么Authenticator是怎么将认证委托给Realm的?

Shiro Realm 简介 - 图2

查看源码得知:在shiro初始化时,Realm被设置到了Authenticator中。

认证策略

SecurityManager 接口继承了 Authenticator,另外还有一个 ModularRealmAuthenticator实现,其委托给多个Realm 进行验证,验证规则通过 AuthenticationStrategy 接口指定。

1、AuthenticationStrategy 接口的默认实现:
 FirstSuccessfulStrategy:只要有一个 Realm 验证成功即可,只返回第 一个 Realm 身份验证成功的认证信息,其他的忽略;
 AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和 FirstSuccessfulStrategy 不同,将返回所有Realm身份验证成功的认证信 息;
 AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有 Realm身份验证成功的认证信息,如果有一个失败就失败了。
 ModularRealmAuthenticator 默认是 AtLeastOneSuccessfulStrategy 策略。

2、在Spring配置文件中修改认证策略

  1. <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
  2. <property name="authenticationStrategy">
  3. <bean class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"></bean>
  4. </property>
  5. </bean>