如何在Shiro和CAS中使用单独的领域进行身份验证和授权? [英] How to use separate realms for authentication and authorization with Shiro and CAS?

查看:323
本文介绍了如何在Shiro和CAS中使用单独的领域进行身份验证和授权?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在处理一个Web应用程序,其中多个应用程序通过CAS SSO服务器进行身份验证.但是,每个应用程序都应保持各自的角色,并且这些角色存储在特定于该应用程序的数据库中.因此,我需要有2个领域,一个领域用于CAS(用于authc),另一个领域用于DB(用于authz).

I'm working on a web application where multiple applications authenticates through a CAS SSO Server. Howerver, each application should maintain their respective roles and these roles are stored in a database specific to the application. So, I need to have 2 realms, one for CAS (for authc) and another for DB (for authz).

这是我当前的Shiro配置.我正在重定向到CAS正常工作,但是登录的用户(Subject)似乎没有加载角色/权限(例如SecurityUtil.isPermitted()无法按预期工作)

This is my current shiro config. I'm getting the redirection to the CAS working properly, but the logged in user (Subject) doesn't seems to have the roles/permission loaded in it (e.g. SecurityUtil.isPermitted() not working as expected)

<bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">
        <property name="name" value="jdbcRealm" />
        <property name="dataSource" ref="dataSource" />
        <property name="authenticationQuery"
            value="SELECT password FROM system_user_accounts WHERE username=? and status=10" />
        <property name="userRolesQuery"
            value="SELECT role_code FROM system_roles r, system_user_accounts u, system_user_roles ur WHERE u.user_id=ur.user_id AND r.role_id=ur.role_id AND u.username=?" />
        <property name="permissionsQuery"
            value="SELECT code FROM system_roles r, system_permissions p, system_role_permission rp WHERE r.role_id=rp.role_id AND p.permission_id=rp.permission_id AND r.role_code=?" />

        <property name="permissionsLookupEnabled" value="true"></property>
        <property name="cachingEnabled" value="true" />
        <property name="credentialsMatcher" ref="passwordMatcher" />
    </bean>

    <!-- For CAS -->
    <bean id="casRealm" class="org.apache.shiro.cas.CasRealm">
        <property name="defaultRoles" value="ROLE_USER" />
        <property name="casServerUrlPrefix" value="http://localhost:7080/auth" />
        <property name="casService" value="http://localhost:8080/hawk-hck-web/shiro-cas" />
        <property name="validationProtocol" value="SAML" />
        <property name="cachingEnabled" value="true"></property>
    </bean>
    <bean id="casSubjectFactory" class="org.apache.shiro.cas.CasSubjectFactory" />

<!-- Security Manager -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realms">
            <list>
                <ref bean="casRealm" />
                <ref bean="jdbcRealm" />
            </list>
        </property>
        <property name="cacheManager" ref="cacheManager"/>
        <property name="subjectFactory" ref="casSubjectFactory" />
    </bean>

<bean id="casFilter" class="org.apache.shiro.cas.CasFilter">
        <property name="failureUrl" value="/error"></property>
    </bean>

<!-- Shiro filter -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="http://localhost:7080/auth/login?service=http://localhost:8080/hawk-hck-web/shiro-cas" />
        <property name="successUrl" value="/home/index" />
        <property name="unauthorizedUrl" value="/error" />
        <property name="filters">
            <util:map>
                    <entry key="casFilter" value-ref="casFilter" /> 
            </util:map>
        </property>
        <property name="filterChainDefinitions">
            <value> 
                <!-- !!! Order matters !!! -->
                /shiro-cas = casFilter
                /login = anon
                /logout = logout
                /error = anon
                /static/** = anon
                /** = authc
            </value>
        </property>
    </bean>

我在securityManager中注册领域的方式应该正确.我真的找不到合适的设置示例.

The way I register the realms with the securityManager should be in correct. I can't really find a good example of the setup.

我在这里有2个问题:

  1. 要实现上述方案,正确的设置/配置是什么?
  2. 在不同/分离的应用程序中管理用户和角色的最佳实践是什么?

推荐答案

您遇到的问题与CasRealm和JdbcRealm都扩展了AuthorizingRealm(Authorizer)和AuthenticatingRealm的事实有关.我要采取的第一步是使用JdbcRealm. JdbcRealm实现继承了

The problem you are running into has to do with the fact that both CasRealm and JdbcRealm extends both AuthorizingRealm (Authorizer) and AuthenticatingRealm. First step I would take is with the JdbcRealm. The JdbcRealm implementation inherits the AuthenticatingRealm#supports(AuthenticationToken token) method implementation. If you extend JdbcRealm and override the "supports" method to return "false" for all token types the JdbcRealm will no longer be used for authentication purposes.

@Override
public boolean supports (AuthenticationToken token) {
    return false;
}

CasRealm是一个不同的故事,(我所知道的)没有办法轻松告诉Shiro不要使用实现

The CasRealm is a different story, there is no way (that I know of) to easily tell Shiro to not use a realm that implements Authorizer when checking permissions. I personally find it frustrating that the default implementation for most protocols assumes that both authorization and authentication are needed. I would prefer each to be split into two implementations (eg AuthenticatingCasRealm, AuthorizingCasRealm).

使用多个领域时检查权限的逻辑是在此处记录.引用此行为的特定文本是:

The logic behind checking permissions when multiple realms are in use is documented here. The specific text that references this behavior is:

第4步:检查每个已配置的Realm,以查看其是否实现了 相同的授权者界面.如果是这样,则该领域自己的hasRole *, 会调用checkRole *,isPermitted *或checkPermission *方法.

Step 4: Each configured Realm is checked to see if it implements the same Authorizer interface. If so, the Realm's own respective hasRole*, checkRole*, isPermitted*, or checkPermission* method is called.

基于此,从理论上讲,您可以覆盖每个命名方法及其所有重载实现,以始终返回"false".

Based on this, you theoretically could override each of the named methods and all of their overloaded implementations to always return "false".

我对这个问题的解决方案是基于我先前关于将每个领域分为两个组成部分,一个用于身份验证,另一个用于授权的评论.这样您最终将获得更多重复的代码,但是在实现中期望的行为是明确的.

My solution to this problem is based on my prior comment about splitting each realm into two components, one for authentication and one for authorization. You end up with more duplicate code this way but it is explicit in what behaviors you are expecting from your implementation.

这是解决方法:

  1. 创建一个新类"AuthenticatingCasRealm",该类扩展了org.apache.shiro.realm.AuthenticatingRealm并实现了org.apache.shiro.util.Initializable.

  1. Create a new class "AuthenticatingCasRealm" that extends org.apache.shiro.realm.AuthenticatingRealm and implements org.apache.shiro.util.Initializable.

复制并粘贴现有

Copy and paste the contents of the existing CasRealm source into your new "AuthenticatingCasRealm" class. (I am aware that taking a copy-and-paste route of existing code is often frowned upon however in the described circumstsance I know of no other way of solving the problem.)

去除为org.apache.shiro.realm.AuthorizingRealm实现的所有方法.

Strip out all methods that were implemented for org.apache.shiro.realm.AuthorizingRealm.

更新您的Shrio配置以引用新的AuthenticatingCasRealm实现.

Update your Shrio configuration to reference your new AuthenticatingCasRealm implementation.

基于这些更改,您现在应该在Shrio配置中具有两个自定义实现;一种是JdbcRealm重写"supports"方法,另一种是CasRealm删除授权API方法.

Based on these changes you should now have two custom implementations in your Shrio config; one of JdbcRealm overriding the "supports" method and one of CasRealm removing the authorization API methods.

有一种其他方法基于通过Shiro的配置明确声明了一个授权者,它可能更适合您的情况.

There is one additional method based on explicitly declaring an Authorizer via Shiro's configuration that may be better suited to your situation.

这里是通过自定义ShiroFilter扩展名对Authorizer和Authenticator的显式声明.两者都已实现,并在启动时注册到提供的JNDI名称.

Here is an explicit declaration of an Authorizer and Authenticator via a custom ShiroFilter extension. Both were implemented and registered to the provided JNDI names at startup.

public class CustomShiroFilter extends ShiroFilter {

    @Override
    public void init () throws Exception {
        super.init();
        DefaultWebSecurityManager dwsm = (DefaultWebSecurityManager) getSecurityManager();
        dwsm.setAuthorizer((Authorizer)JndiUtil.get("realms/authorizerRealm"));
        dwsm.setAuthenticator((Authenticator)JndiUtil.get("realms/authenticatorRealm"));
    }
}

这篇关于如何在Shiro和CAS中使用单独的领域进行身份验证和授权?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆