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文件夹。(删除前请备份。)

cas的单点登录实现的更多相关文章

  1. 如何利用tomcat和cas实现单点登录(1):配置tomcat的ssl和部署cas

    如何利用tomcat和cas实现单点登录,借鉴了网上的很多教程,主要分为以下几个步骤: 一:下载好cas,tomcat之后,首先配置tomcat: 用鼠标右键点击"计算机"→选择& ...

  2. CAS实现单点登录流程

    CAS实现单点登录 环境 客户端: www.app1.com CAS服务器: www.cas-server.com 1.浏览器:发起请求 www.app1.com 2. 客户端:Authenticat ...

  3. CAS实现单点登录SSO执行原理及部署

    一.不落俗套的开始 1.背景介绍 单点登录:Single Sign On,简称SSO,SSO使得在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统. CAS框架:CAS(Centra ...

  4. [精华][推荐]CAS SSO 单点登录框架学习 环境搭建

    1.了解单点登录  SSO 主要特点是: SSO 应用之间使用 Web 协议(如 HTTPS) ,并且只有一个登录入口. SSO 的体系中有下面三种角色: 1) User(多个) 2) Web 应用( ...

  5. CAS SSO单点登录框架学习

    1.了解单点登录  SSO 主要特点是: SSO 应用之间使用 Web 协议(如 HTTPS) ,并且只有一个登录入口. SSO 的体系中有下面三种角色: 1) User(多个) 2) Web 应用( ...

  6. [精华][推荐]CAS SSO单点登录服务端客户端实例

    1.修改server.xml文件,如下: 注意: 这里使用的是https的认证方式,需要将这个配置放开,并做如下修改: <Connector port="8443" prot ...

  7. CAS的单点登录和oauth2的最大区别

    CAS的单点登录时保障客户端的用户资源的安全 oauth2则是保障服务端的用户资源的安全 CAS客户端要获取的最终信息是,这个用户到底有没有权限访问我(CAS客户端)的资源. oauth2获取的最终信 ...

  8. Spring Security 集成CAS实现单点登录

    参考:http://elim.iteye.com/blog/2270446 众所周知,Cas是对单点登录的一种实现.本文假设读者已经了解了Cas的原理及其使用,这些内容在本文将不会讨论.Cas有Ser ...

  9. CAS SSO单点登录框架介绍

    1.了解单点登录  SSO 主要特点是: SSO 应用之间使用 Web 协议(如 HTTPS) ,并且只有一个登录入口. SSO 的体系中有下面三种角色: 1) User(多个) 2) Web 应用( ...

  10. 基于CAS的单点登录实战(2)-- 搭建cas的php客户端

    在这之前已经搭好了CAS服务端 基于CAS的单点登录实战(1)-- 搭建cas服务器 PHP-Client php-Client是官方支持的,去官网下个最新版就好了.phpCAS 接入很简单,解压放到 ...

随机推荐

  1. 数据库接口基础类 oracle,sql server

    1.为数据库读取基类 public class DBBase : IDisposable { public virtual void Dispose() { throw new NotImplemen ...

  2. C语言-指针深度分析

    1.变量回顾 程序中的变量只是—段存储空间的别名,那么是不 是必须通过这个别名才能使用这段存储空间? 2.思考 下面的程序输出什么?为什么? ;    int* p = &i;       p ...

  3. 在多租户(容器)数据库中如何创建PDB:方法3 克隆远程PDB

    基于版本:19c (12.2.0.3) AskScuti 创建方法:克隆远程PDB(从非当前CDB中进行远程克隆).将 CDB2 中的 ERP1 远程克隆为 CDB1 中的 PDB6 对应路径:Cre ...

  4. Vue中import '@...'的意思

    转载: https://blog.csdn.net/xiazeqiang2018/article/details/81325996 写项目的时候看到很多导入都是@开头,这是webpack的路径别名,相 ...

  5. WEB-INF目录与META-INF目录的作用(转载)

    /WEB-INF/web.xml Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则. /WEB-INF/classes/包含了站点所有用的 class 文件,包括 ser ...

  6. 515,前端性能优化--减少http请求(待补充)

    对于影响页面呈选的因素有三个地方:服务器连接数据库并计算返回数据,http请求以及数据(文件)经过网络传输,文件在浏览器中计算渲染呈选:其中大约80%的时间都消耗在了http的请求上,所以要想大幅度的 ...

  7. MySql 怎么存取 Emoji

    01.前言 Emoji 在我们生活中真的是越来越常见了,几乎每次发消息的时候不带个 Emoji,总觉得少了点什么,似乎干巴巴的文字已经无法承载我们丰富的感情了.对于我们开发者来说,如何将 Emoji ...

  8. python接口自动化测试 - requests库的基础使用

    简单介绍 requests库简单易用的HTTP库 Get请求 格式: requests.get(url) 注意:若需要传请求参数,可直接在 url 最后的 ? 后面,也可以调用 get() 时多加一个 ...

  9. SpringCloud Netflix Ribbon

    Eureka客户端的负载均衡:从Eureka server拿到节点列表,客户端自己使用负载均衡的算法确定要使用的节点: Eureka服务端的负载均衡:服务端使用负载均衡的算法,从节点列表中确定要使用的 ...

  10. 静态方法使用synchronized修饰.

    package seday10;/** * @author xingsir * 静态方法若使用synchronized修饰,这个方法一定具有同步效果.静态方法上使用的同步监视器对象为这个类的" ...