cas的單點登錄實現


 

1.  前提條件

環境:jdk1.8、shiro1.4.0及以上版本、項目以 spring+shiro構建

工具:buji-pac4j-3.1.0-jar-with-dependencies.jar以及相關配置文件

從網上下載cas項目源碼

client為客戶端代碼,server為服務端代碼。

將buji-pac4j-3.1.0導入eclipse,eclipse須裝Maven插件,項目右鍵:

Run As --> Run Configurations

打開彈窗,Name一欄填buji-pac4j-3.1.0-jar-with-dependencies,

Base directory一欄選擇buji-pac4j-3.1.0的路徑,

Goals一欄填package,點擊Run即可生成buji-pac4j-3.1.0-jar-with-dependencies.jar

2.  導入相關文件

在項目的WEB-INF的lib下導入buji-pac4j-3.1.0-jar-with-dependencies.jar.

    引入cas.properties(在編譯器中請放在resources下,tomcat中放在WEB-INF的class下)。

3.  修改相關聯的配置文件。

  1. 修改web.xml增加context-param

<!--本地登錄開關-->
<context-param>
    <param-name>pac4jConfigLocation</param-name>
    <param-value>classpath:cas.properties</param-value>
</context-param>

  1. 修改web.xml增加listener

<listener>
    <listener-class>io.buji.pac4j.extension.listener.Pac4jServletContextListener</listener-class>
</listener>

放在Spring容器加載之后例如:

<!-- 加載Spring容器配置  -->
<listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<listener>
       <listener-class>io.buji.pac4j.extension.listener.Pac4jServletContextListener</listener-class>
   </listener>

4  在shiro中追加相關配置

     <!--追加 start-->
<bean id="annotationProxy"
      class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
      depends-on="lifecycleBeanPostProcessor">
    <property name="proxyTargetClass" value="true" />
</bean>

<bean id="casRealm" class="io.buji.pac4j.extension.realm.SSOCasRealm">
    <property name="cachingEnabled" value="false" />
    <property name="authenticationCachingEnabled" value="false" />
    <property name="authenticationCacheName" value="authenticationCache" />
    <property name="authorizationCachingEnabled" value="false" />
    <property name="authorizationCacheName" value="authorizationCache" />
</bean>
<!--追加 end-->

   depends-on 需要Shiro生命周期處理器的id。例如:

<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

   depends-on 需要的這個bean的id。

自定義casRealm實現認證和授權

<bean id="casRealm" class="io.buji.pac4j.extension.realm.SSOCasRealm">
    <property name="cachingEnabled" value="false" />
    <property name="authenticationCachingEnabled" value="false" />
    <property name="authenticationCacheName" value="authenticationCache" />
    <property name="authorizationCachingEnabled" value="false" />
    <property name="authorizationCacheName" value="authorizationCache" />
</bean>

       請繼承Pac4jRealm,bean的ID不要變例如:


public class Pac4jRealm extends io.buji.pac4j.realm.Pac4jRealm {

    @Autowired
    private UserService userService;
    private String principalNameAttribute;

    @Autowired
    protected UserRoleService userRoleService;
    @Autowired
    protected OrganizationRoleService organizationRoleService;
    @Autowired
    protected ModuleService moduleService;
    @Autowired
    protected UserCasService userCasService;

    // 是否啟用超級管理員
    protected boolean activeRoot = true;

    public String getPrincipalNameAttribute() {
        return this.principalNameAttribute;
    }

    public void setPrincipalNameAttribute(String principalNameAttribute) {
        this.principalNameAttribute = principalNameAttribute;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        Pac4jToken token = (Pac4jToken)authenticationToken;
        LinkedHashMap<String, CommonProfile> profiles = token.getProfiles();
        String username = profiles.get("SSOCasClient").getId();
        User user = userService.getByUsername(username);
        ShiroUser shiroUser = new ShiroUser(user.getId(), user.getUsername(), user);

        Object credentials = token.getCredentials();
       
        return new SimpleAuthenticationInfo(shiroUser,credentials,getName());
    }


    /**
     *
授權查詢回調函數, 進行鑒權但緩存中無用戶的授權信息時調用.
     */
   
@Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)
    {
        Collection<?> collection = principals.fromRealm(getName());
        if (Collections3.isEmpty(collection))
        {
            return null;
        }
        ShiroUser shiroUser = (ShiroUser)collection.iterator().next();

       /* SimplePrincipalCollection collection =(SimplePrincipalCollection) principals.getPrimaryPrincipal();
        String userName =(String) collection.getPrimaryPrincipal();
        User user = userService.getByUsername(userName);*/

        List<UserRole> userRoles = userRoleService.find(shiroUser.getId());
        List<OrganizationRole> organizationRoles =
                organizationRoleService.find(shiroUser.getUser().getOrganization().getId());
        //ShiroUser shiroUser = new ShiroUser(user.getId(), user.getUsername(), user);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermissions(makePermissions(userRoles, organizationRoles, shiroUser));

        return info;
    }

    private Collection<String> makePermissions(List<UserRole> userRoles, List<OrganizationRole> organizationRoles,
                                               ShiroUser shiroUser)
    {
        // 是否啟用超級管理員
        if (activeRoot)
        {
            // 為超級管理員,構造所有權限
            if (userCasService.isSupervisor(shiroUser.getId()))
            {
                Collection<String> stringPermissions = Sets.newHashSet();

                List<Module> modules = moduleService.findAll();
                for (Module module : modules)
                {
                    List<Permission> permissions = module.getPermissions();
                    // 默認構造CRUD權限
                    stringPermissions.add(module.getSn() + ":" + Permission.PERMISSION_CREATE);
                    stringPermissions.add(module.getSn() + ":" + Permission.PERMISSION_READ);
                    stringPermissions.add(module.getSn() + ":" + Permission.PERMISSION_UPDATE);
                    stringPermissions.add(module.getSn() + ":" + Permission.PERMISSION_DELETE);

                    for (Permission permission : permissions)
                    {
                        stringPermissions.add(module.getSn() + ":" + permission.getShortName());
                    }
                }

                //log.info("使用了超級管理員:" + shiroUser.getLoginName() + "登錄了系統。At " + new Date());
                //log.info(shiroUser.getLoginName() + "擁有的權限:" + stringPermissions);
                return stringPermissions;
            }
        }

        Set<Role> roles = Sets.newHashSet();
        for (UserRole userRole : userRoles)
        {
            roles.add(userRole.getRole());
        }

        for (OrganizationRole organizationRole : organizationRoles)
        {
            roles.add(organizationRole.getRole());
        }

        Collection<String> stringPermissions = Sets.newHashSet();
        for (Role role : roles)
        {
            List<RolePermission> rolePermissions = role.getRolePermissions();
            for (RolePermission rolePermission : rolePermissions)
            {
                Permission permission = rolePermission.getPermission();
                stringPermissions.add(permission.getModule().getSn() + ":" + permission.getShortName());
            }
        }

        //    log.info(shiroUser.getLoginName() + "擁有的權限:" + stringPermissions);
        return stringPermissions;
    }



}

      可以直接仿造自己項目中的realm寫,不一定要按照以上的Demo寫。

5 修改cas.properties

##cas服務前綴
sso.cas.server.prefixUrl=http://127.0.0.1:8081/cas
##cas服務登錄url
sso.cas.server.loginUrl=http://127.0.0.1:8081/cas/login

##cas客戶端回調地址
sso.cas.client.callbackUrl=http://127.0.0.1:8080/dts/callback?client_name=SSOCasClient
##cas服務端成功跳轉地址
sso.cas.client.successUrl=http://127.0.0.1:8080/dts/index

##cas登出地址
sso.cas.client.logoutUrl=http://127.0.0.1:8081/cas/logout


##本地登錄地址
sso.cas.client.nativeLoginUrl=http://127.0.0.1:8080/dts/login

##securityManagerId
securityManagerId=securityManager

##shiro配置filterChainDefinitions中定義的logout
logoutUrl=/logout

##shiro配置filterChainDefinitions中的最后一個匿名訪問地址
lastAnonUrl =/zui*/**

##shiro配置的org.apache.shiro.spring.web.ShiroFilterFactoryBean的id
originFilter=shiroFilter

##pac4j配置的org.apache.shiro.spring.web.ShiroFilterFactoryBean的id
ssoFilter=myfilter

##是否啟用開關
#1只能單點登錄  0全開啟 -1 只能本地登錄
loadFlag =1

這個Demo使用的cas服務端的登錄地址為:http://127.0.0.1:8081/cas/login

以上屬性名不要改變,只配置屬性值,ssoFilter這個屬性不用配置

6 服務端打war包及部署。

 在IDEA里導入服務端項目cas\server\cas-4.2.1,

用Gradle插件刷新顯示所有子項目,找到cas-server-webapp,

點開cas-server-webapp --> Tasks --> build,先點擊clean再點擊war,

等待運行結束,在cas-server-webapp項目下build下libs里生成了war包。

將war包部署至服務器,訪問該項目即可。

 

7 常見問題解決。

1.項目部署之后, One or more listeners failed to start. Full details will be found in the appropriate container log file。

這個問題一般來是jar包沖突或版本不對。常見的就是Shiro的jar包版本過低。請升級到1.4或以上版本。還有一種情況,guava版本不同,可以嘗試下,刪除buji-pac4j-3.1.0-jar-with-dependencies.jar中的com文件夾。(刪除前請備份。)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM