l3 / cas-guard-bundle
适用于 Symfony3, Symfony4, Symfony5, Symfony6, Symfony7 的 PHPCas 包装器
Requires
- php: >=8.2
- apereo/phpcas: 1.6.1
- symfony/framework-bundle: ~7.0
- symfony/security-bundle: ~7.0
README
Symfony Cas Guard Bundle
此包是基于经典 jasig/phpCAS 库的依赖性包装器。
支持单点登出(BeSimpleSSoBundle 不支持)。
安装
使用以下命令安装包
composer require l3/cas-guard-bundle:~1.0
在 Symfony 的 Kernel 中声明 Bundle
对于 Symfony3,将 Bundle 添加到 app/AppKernel.php 中
<?php
// app/AppKernel.php
// ...
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
// ...
new L3\Bundle\CasGuardBundle\L3CasGuardBundle(),
);
// ...
}
// ...
}
对于 Symfony4、Symfony5、Symfony6 和 Symfony7,将 Bundle 添加到 config/bundles.php 中(如果不存在行)
<?php
return [
...
L3\Bundle\CasGuardBundle\L3CasGuardBundle::class => ['all' => true],
...
];
Bundle 配置
对于 Symfony3,将 l3_cas_guard 参数添加到您的配置文件(parameters.yml 和 parameters.yml.dist)中
l3_cas_guard:
host: cas-test.univ-lille3.fr # Cas Server
path: ~ # App path if not in root (eg. cas.test.com/cas)
port: 443 # Server port
ca: false # SSL Certificate
handleLogoutRequest: true # Single sign out activation (default: false)
casLogoutTarget: https://ent-test.univ-lille3.fr # Redirect path after logout
force: true # Allows cas check mode and not force, user : __NO_USER__ if not connected (If force false, Single sign out cant work).
gateway: true # Gateway mode (for use the mode gateway of the Cas Server) set to false if you use micro-services or apis rest.
对于 Symfony4、Symfony5 和 Symfony6,将变量添加到您的配置文件(.env 和 .env.dist)中
...
###> l3/cas-guard-bundle ###
CAS_HOST=cas-test.univ-lille3.fr # Cas Server
CAS_PATH=~ # App path if not in root (eg. cas.test.com/cas)
CAS_PORT=443 # Server port
CAS_CA=false # SSL Certificate
CAS_HANDLE_LOGOUT_REQUEST=true # Single sign out activation (default: false)
CAS_LOGIN_TARGET=https://server.univ-lille3.fr # Redirect path after login (when use anonymous mode)
CAS_LOGOUT_TARGET=https://ent-test.univ-lille3.fr # Redirect path after logout
CAS_FORCE=true # Allows cas check mode and not force, user : __NO_USER__ if not connected (If force false, Single sign out cant work).
CAS_GATEWAY=true # Gateway mode (for use the mode gateway of the Cas Server) set to false if you use micro-services or apis rest.
###< l3/cas-guard-bundle ###
...
并在您的 config/services.yml 文件中添加参数(在 parameters 下)
...
parameters:
cas_login_target: '%env(string:CAS_LOGIN_TARGET)%'
cas_logout_target: '%env(string:CAS_LOGOUT_TARGET)%'
cas_host: '%env(string:CAS_HOST)%'
cas_port: '%env(int:CAS_PORT)%'
cas_path: '%env(string:CAS_PATH)%'
cas_gateway: '%env(bool:CAS_GATEWAY)%'
l3_cas_guard:
host: '%env(string:CAS_HOST)%'
path: '%env(string:CAS_PATH)%'
port: '%env(int:CAS_PORT)%'
ca: '%env(bool:CAS_CA)%'
handleLogoutRequest: '%env(bool:CAS_HANDLE_LOGOUT_REQUEST)%'
casLogoutTarget: '%env(string:CAS_LOGOUT_TARGET)%'
force: '%env(bool:CAS_FORCE)%'
gateway: '%env(bool:CAS_GATEWAY)%'
...
对于 Symfony7,将变量添加到您的配置文件(.env 和 .env.dist)中
...
###> l3/cas-guard-bundle ###
CAS_HOST=cas-test.univ-lille3.fr # Cas Server
CAS_PATH=~ # App path if not in root (eg. cas.test.com/cas)
CAS_PORT=443 # Server port
CAS_CA=false # SSL Certificate
CAS_HANDLE_LOGOUT_REQUEST=true # Single sign out activation (default: false)
CAS_SERVICE_BASE_URL=https://server.univ-lille3.fr # Service base URL how authorized to consume server cas
CAS_LOGIN_TARGET=https://server.univ-lille3.fr # Redirect path after login (when use anonymous mode)
CAS_LOGOUT_TARGET=https://ent-test.univ-lille3.fr # Redirect path after logout
CAS_FORCE=true # Allows cas check mode and not force, user : __NO_USER__ if not connected (If force false, Single sign out cant work).
CAS_GATEWAY=true # Gateway mode (for use the mode gateway of the Cas Server) set to false if you use micro-services or apis rest.
###< l3/cas-guard-bundle ###
...
并在您的 config/services.yml 文件中添加参数(在 parameters 下)
...
parameters:
cas_login_target: '%env(string:CAS_LOGIN_TARGET)%'
cas_logout_target: '%env(string:CAS_LOGOUT_TARGET)%'
cas_host: '%env(string:CAS_HOST)%'
cas_port: '%env(int:CAS_PORT)%'
cas_path: '%env(string:CAS_PATH)%'
cas_gateway: '%env(bool:CAS_GATEWAY)%'
l3_cas_guard:
host: '%env(string:CAS_HOST)%'
path: '%env(string:CAS_PATH)%'
port: '%env(int:CAS_PORT)%'
ca: '%env(bool:CAS_CA)%'
handleLogoutRequest: '%env(bool:CAS_HANDLE_LOGOUT_REQUEST)%'
casServiceBaseUrl: '%env(string:CAS_SERVICE_BASE_URL)%'
casLogoutTarget: '%env(string:CAS_LOGOUT_TARGET)%'
force: '%env(bool:CAS_FORCE)%'
gateway: '%env(bool:CAS_GATEWAY)%'
...
安全性配置
对于 Symfony3、Symfony4 或 Symfony5,在 app/config/security.yml 中的安全文件中配置防火墙
security:
providers:
# ...
firewalls:
dev:
pattern: ^/(_(profiler|wdt|error)|css|images|js)/
security: false
l3_firewall:
pattern: ^/
security: true
guard:
authenticators:
- cas.security.authentication.authenticator
对于 Symfony6 和 Symfony7
security:
providers:
# ...
firewalls:
dev:
pattern: ^/(_(profiler|wdt|error)|css|images|js)/
security: false
l3_firewall:
pattern: ^/
security: true
custom_authenticators:
- cas.security.authentication.authenticator
匿名配置
请注意,如果您想使用匿名模式,则 bundle cas 使用登录 NO_USER,使用以下安全设置
security: providers: chain_provider: chain: providers: [in_memory, your_userbundle] in_memory: memory: users: __NO_USER__: password: roles: ROLE_ANON your_userbundle: id: your_userbundle
在 Symfony4 中,如果您使用 chain_provider,则应在 config/packages/security.yaml 中的所有条目(例如 l3_firewall 和 main)防火墙(在 security 激活的地方:security: true)上设置提供者名称,如下所示
# config/packages/security.yaml
security:
providers:
chain_provider:
chain:
providers: [in_memory, your_userbundle]
in_memory:
memory:
users:
__NO_USER__:
password:
roles: ROLE_ANON
your_userbundle:
id: your_userbundle
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
l3_firewall:
pattern: ^/
security: true
provider: chain_provider
guard:
authenticators:
- cas.security.authentication.authenticator
main:
pattern: ^/
security: true
anonymous: true
provider: chain_provider
guard:
authenticators:
- cas.security.authentication.authenticator
接下来在 app/config/parameters.yml(对于 Symfony3)和 config/services.yaml(对于 Symfony4)中将 force 设置为 false
l3_cas_guard:
...
force: false # Allows cas check mode and not force, user : __NO_USER__ if not connected (If force false, Single sign out cant work).
对于 Symfony3,在 app/config/security.yml 中将 default: anonymous
security:
providers:
# ...
firewalls:
dev:
pattern: ^/(_(profiler|wdt|error)|css|images|js)/
security: false
l3_firewall:
pattern: ^/
security: true
guard:
authenticators:
- cas.security.authentication.authenticator
default:
anonymous: ~
guard:
authenticators:
- cas.security.authentication.authenticator
对于 Symfony4,在 config/packages/security.yaml 中将 main: anonymous
security:
providers:
# ...
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
l3_firewall:
pattern: ^/
security: true
guard:
authenticators:
- cas.security.authentication.authenticator
main:
anonymous: ~
pattern: ^/
security: true
guard:
authenticators:
- cas.security.authentication.authenticator
对于 Symfony 5,将 anonymous: true 替换为 lazy: true,如下所示
main:
pattern: ^/
security: true
lazy: true
provider: chain_provider
guard:
authenticators:
- cas.security.authentication.authenticator
对于 Symfony 6 和 Symfony7,将 anonymous: true 替换为 lazy: true,如下所示
main:
pattern: ^/
security: true
lazy: true
provider: chain_provider
custom_authenticators:
- cas.security.authentication.authenticator
对于 Symfony3,在 app/config/parameters.yml.dist 和 app/config/parameters.yml 下的参数中添加 cas_host、cas_login_target、cas_path 和 cas_gateway
...
cas_login_target: https://your_web_path_application.com/
cas_logout_target: https://your_web_path_application.com/
cas_host: cas-test.univ-lille3.fr
cas_path: ~
cas_gateway: true
...
对于 Symfony4、Symfony5、Symfony6 和 Symfony7,在 config/services.yaml 下的参数中添加 cas_host 和 cas_login_target
...
cas_login_target: '%env(string:CAS_LOGIN_TARGET)%'
cas_logout_target: '%env(string:CAS_LOGIN_TARGET)%'
cas_host: '%env(string:CAS_HOST)%'
cas_port: '%env(int:CAS_PORT)%'
cas_path: '%env(string:CAS_PATH)%'
cas_gateway: '%env(bool:CAS_GATEWAY)%'
...
对于 Symfony 3 和 Symfony 4,在您的应用程序的 DefaultController 中创建登录路由和强制路由
/**
* @Route("/login", name="login")
*/
public function loginAction() {
$url = 'https://'.$this->container->getParameter('cas_host') . $this->container->getParameter('cas_path') . '/login?service=';
$target = $this->container->getParameter('cas_login_target');
return $this->redirect($url . urlencode($target . '/force'));
}
/**
* @Route("/force", name="force")
*/
public function forceAction() {
if ($this->container->getParameter('cas_gateway')) {
if (!isset($_SESSION)) {
session_start();
}
session_destroy();
}
return $this->redirect($this->generateUrl('homepage'));
}
对于 Symfony 5,在您的应用程序的 DefaultController 中创建登录路由和强制路由
/**
* @Route("/login", name="login")
*/
public function login(Request $request) {
$url = 'https://'.$this->getParameter('cas_host') . $this->getParameter('cas_path') . '/login?service=';
$target = $this->getParameter('cas_login_target');
return $this->redirect($url . urlencode($target . '/force'));
}
/**
* @Route("/force", name="force")
*/
public function force(Request $request) {
if ($this->getParameter('cas_gateway')) {
if (!isset($_SESSION)) {
session_start();
}
session_destroy();
}
return $this->redirect($this->generateUrl('index'));
}
对于 Symfony 6,创建此控制器 src/Controller/DefaultController.php
<?php
namespace App\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class DefaultController extends AbstractController
{
/**
* @Route("/login", name="login")
*/
public function login(Request $request) {
$target = urlencode($this->getParameter('cas_login_target').'/force');
$url = 'https://'.$this->getParameter('cas_host') . ((($this->getParameter('cas_port')!=80) || ($this->getParameter('cas_port')!=443)) ? ":".$this->getParameter('cas_port') : "") . $this->getParameter('cas_path') . '/login?service=';
return $this->redirect($url . $target);
}
/**
* @Route("/logout", name="logout")
*/
public function logout(Request $request) {
if (($this->getParameter('cas_logout_target') !== null) && (!empty($this->getParameter('cas_logout_target')))) {
\phpCAS::logoutWithRedirectService($this->getParameter('cas_logout_target'));
} else {
\phpCAS::logout();
}
}
/**
* @Route("/force", name="force")
*/
public function force(Request $request) {
if ($this->getParameter("cas_gateway")) {
if (!isset($_SESSION)) {
session_start();
}
session_destroy();
}
return $this->redirect($this->generateUrl('index'));
}
/**
* @Route("/", name="index")
*/
public function index(Request $request) : Response
{
dump($this->container->get('security.token_storage'));
dump($this->getUser());
return $this->render('base.html.twig', []);
}
}
对于 Symfony 7,创建此控制器 src/Controller/DefaultController.php
<?php
namespace App\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class DefaultController extends AbstractController
{
#[Route('/login', name:'login', methods: ['GET'])]
public function login(Request $request) {
$target = urlencode($this->getParameter('cas_login_target').'/force');
$url = 'https://'.$this->getParameter('cas_host') . ((($this->getParameter('cas_port')!=80) || ($this->getParameter('cas_port')!=443)) ? ":".$this->getParameter('cas_port') : "") . $this->getParameter('cas_path') . '/login?service=';
return $this->redirect($url . $target);
}
#[Route('/logout', name:'logout', methods: ['GET'])]
public function logout(Request $request) {
if (($this->getParameter('cas_logout_target') !== null) && (!empty($this->getParameter('cas_logout_target')))) {
\phpCAS::logoutWithRedirectService($this->getParameter('cas_logout_target'));
} else {
\phpCAS::logout();
}
}
#[Route('/force', name:'force', methods: ['GET'])]
public function force(Request $request) {
if ($this->getParameter("cas_gateway")) {
if (!isset($_SESSION)) {
session_start();
}
session_destroy();
}
return $this->redirect($this->generateUrl('index'));
}
#[Route('/', name:'index', methods: ['GET'])]
public function index(Request $request) : Response
{
dump($this->container->get('security.token_storage'));
dump($this->getUser());
return $this->render('base.html.twig', []);
}
}
最后,您可以使用路由 /login 来调用 cas 登录页面,并将其重定向到您的应用程序,然后您就登录了 :)
单点登出配置
为了使用单点登出,建议在 Symfony 中禁用 Symfony Sessions(这样您将使用 PHP 本地会话)。
# app/config/config.yml (Symfony3)
# config/packages/framework.yaml (for Symfony4 and Symfony5 and Symfony6)
framework:
# ...
session:
handler_id: ~
save_path: ~
信息:该包与 PHPCas 进行检查,以检测一些 PHPCAS 未完全实现的断开连接请求(有关详细信息,请参阅 L3\Bundle\CasGuardBundle\Security\CasAuthenticator::checkHandleLogout())
用户提供者
对于LDAP用户,您可以使用LdapUserBundle(分支ou=people)或LdapUdlUserBundle(分支ou=accounts)。您还可以使用只返回uid的简单UidUserBundle。
您还可以使用FOSUserBundle...,例如://security.yml
providers: chain_provider: chain: providers: [in_memory, fos_userbundle] in_memory: memory: users: __NO_USER__: password: roles: ROLE_ANON fos_userbundle: id: fos_user.user_provider.username
注销路由
在Symfony 3中,如果您想使用/logout路由来调用注销,您可以在您的routing.yml中添加此内容
l3_logout:
path: /logout
defaults: { _controller: L3CasBundle:Logout:logout }
在Symfony 4中,您可以在您的routes.yaml中添加此内容
logout:
path: /logout
controller: L3\Bundle\CasBundle\Controller\LogoutController::logoutAction
在Symfony 5或Symfony6或Symfony7中,您必须在应用程序的DefaultController中创建一个注销路由
/**
* @Route("/logout", name="logout")
*/
public function logoutAction() {
if (($this->getParameter('cas_logout_target') !== null) && (!empty($this->getParameter('cas_logout_target')))) {
\phpCAS::client(CAS_VERSION_2_0, $this->getParameter('host'), $this->getParameter('port'), is_null($this->getParameter('path')) ? '' : $this->getParameter('path'), true);
\phpCAS::logoutWithRedirectService($this->getParameter('cas_logout_target'));
} else {
\phpCAS::client(CAS_VERSION_2_0, $this->getParameter('host'), $this->getParameter('port'), is_null($this->getParameter('path')) ? '' : $this->getParameter('path'), true);
\phpCAS::logout();
}
}
注销处理器
在某些应用程序(如EasyAdminBundle)中,您可能需要使用注销成功处理器,以便EasyAdmin使用此注销成功处理器进行/注销。
- 创建src/Handler/AuthenticationHandler.php,并添加以下代码
<?php
namespace App\Handler;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface;
class AuthenticationHandler implements LogoutSuccessHandlerInterface
{
protected $cas_logout_target;
public function __construct($cas_logout_target)
{
$this->cas_logout_target = $cas_logout_target;
}
public function onLogoutSuccess(Request $request) : Response
{
if(!empty($this->cas_logout_target)) {
\phpCAS::logoutWithRedirectService($this->cas_logout_target);
} else {
\phpCAS::logout();
}
}
}
- 在config/services.yaml中,在"services:"下添加此行
authentication_handler:
class: App\Handler\AuthenticationHandler
arguments: ['%cas_logout_target%']
public: false
- 在config/packages/security.yaml中,添加以下行(对于firewalls下的cas: true)
logout:
path: /logout
success_handler: authentication_handler
invalidate_session: false
附加属性
Jasig Cas服务器可以通过phpCAS::getAttributes()函数返回除主要属性(通常是uid)之外的附加属性。
您可以使用以下代码在控制器中获取附加属性
...
$attributes = $this->get('security.token_storage')->getToken()->getAttributes();
...
对于Symfony6
...
$attributes = $this->container->get('security.token_storage')->getToken()->getAttributes();
...
注释
如果您安装了此包,则Route注释会运行
composer require doctrine/annotations