Symfony Security Cheat Sheet

如果无法正常显示,请先停止浏览器的去广告插件。
分享至:
1. Powerful and complete Security for your apps! Security You can use: - Security Component (Standalone) - SecurityBundle (integrates Security Component on Symfony) 4.4 By https://andreiabohner.org Contains sub-components: Core, Http, Guard, Csrf Security Component (Standalone) Install $ composer require symfony/security-core Who you are Authentication (a token will be generated to represent you) HTTP Security component uses listeners attached to kernel.request to create tokens. Tokens Create a token representing the user input UsernamePasswordToken username and password token RememberMeToken uses a browser cookie SwitchUserToken token representing a user who temporarily impersonates another one AnonymousToken represents an anonymous token use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; $inputToken = new UsernamePasswordToken('john', 'myPassword987', 'default'); often used in traditional apps User Providers ChainUserProvider input from the user Load users from “some resource” use Symfony\Component\Security\Core\User\InMemoryUserProvider; calls several providers in a chain until one is $userProvider = new InMemoryUserProvider([ able to handle the request InMemoryUserProvider UsernamePasswordFormAuthenticationListener creates a UsernamePasswordToken based on the login form submit 'john' => [ simple non persistent user provider. Useful for testing, demonstration, prototyping, and for simple needs 'password' => 'myPassword987', (a backend with a unique admin for instance) 'roles' => ['POST_CREATE'] ], e.g.: fetch users from a PHP array ]); LdapUserProvider user provider on top of LDAP MissingUserProvider dummy user provider used to throw proper exception find the user $myUser = $userProvider ->loadUserByUsername('john'); when a firewall requires a user provider but none was defined You can also create your own custom user provider Encode a plain text password and check if the password is valid Password Encoders use Symfony\Component\Security\Core\Encoder\EncoderFactory; use Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder; Argon2iPasswordEncoder BCryptPasswordEncoder NativePasswordEncoder These encoders do not require a user-generated salt use Symfony\Component\Security\Core\User\User; $encoderFactory = new EncoderFactory([ User::class => new PlaintextPasswordEncoder(), SodiumPasswordEncoder MigratingPasswordEncoder MessageDigestPasswordEncoder Pbkdf2PasswordEncoder PlaintextPasswordEncoder UserPasswordEncoder Hashes passwords using the best available encoder ]); get the encoder associated with this user (other users can use other encoders) $encoderFactory ->getEncoder(User::class) ->isPasswordValid($myUser->getPassword(), 'myPassword987', ''); check if matches the user's password
2. By https://andreiabohner.org Security AuthenticationManagerInterface is responsible for this Authenticate the Token: AuthenticationManager 4.4 AuthenticationProviderManager - authentication manager based on authentication providers: Transform an unauthenticated token ( user input ) into an authenticated token (security identity) Authentication Providers AnonymousAuthenticationProvider validates AnonymousToken instances. Always returns a token representing an anonymous user DaoAuthenticationProvider uses a user provider (UserProviderInterface) to retrieve a user matching the input and then matches most used the password (using a password encoder UsernamePasswordToken). LdapBindAuthenticationProvider authenticates a user against an LDAP server RememberMeAuthenticationProvider authenticates a remember-me cookie SimpleAuthenticationProvider deprecated since Symfony 4.2, use Guard instead Instantiate the Authentication Manager After: - create a token representing the user input - load the user from some User Provider - encode & check the password we can create the AuthenticationProviderManager use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager; use Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider; use Symfony\Component\Security\Core\User\UserChecker; $authenticationManager = new AuthenticationProviderManager([ new DaoAuthenticationProvider( $userProvider , Check some “user flags” after the user is fetched from user provider (e.g. if the user is activated, ...) new UserChecker(), 'default', $encoderFactory ), ]); The provider key (same provided when create the token). Used to make sure the token is from our app and to know which provider should handle the token Authenticate the Token Create an authenticated token $authenticatedToken = $authenticationManager-> authenticate ( $inputToken ); echo 'Hi ' .$authenticatedToken ->getUsername(); what you are allowed to do Authorization (determine whether or not you have access to something) Authorize Actions: AccessDecisionManager The default implementation uses “Security voters” to decide whether the user is allowed to execute an action. These voters are provided with an attribute (representing the action) and optionally some context (the subject of the action). use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter; $accessDecisionManager = new AccessDecisionManager([ this voter checks if new RoleVoter('POST_'), the User’s getRoles() POST_ is the prefix an contains the provided ]); attribute must have in order attribute to be managed by this voter $isSupervisor = $accessDecisionManager->decide( $authenticatedToken , ['POST_CREATE'] ); uses the access decision manager to see if the authenticated token has the “POST_CREATE” role Voters The default Symfony voters don’t validate an action, but validate the user’s identity. AuthenticatedVoter ExpressionVoter RoleVoter RoleHierarchyVoter
3. By https://andreiabohner.org Security Install $ composer require symfony/security-bundle Symfony (SecurityBundle ) 4.4 Integrates the Security Component on Symfony apps Authentication The User Class namespace Symfony\Component\Security\Core\User; use Symfony\Component\Security\Core\Role\Role; The easiest way to create the User class is to use the MakerBundle interface UserInterface { $ php bin/console make:user /** The name of the security user class (e.g. User) [User]: * Returns the roles granted to the user. Call the class: User > User * Do you want to store user data in the database (via Doctrine)? (yes/no)[yes]: * public function getRoles() > yes * { e.g. config to store user info in the database * return ['ROLE_USER']; Enter a property name that will be the unique "display" name for the user * (e.g. email, username, uuid [email] * > email * Alternatively, the roles might be stored on a ``roles`` property, } * and populated in any number of different ways when the user object Does this app need to hash/check user passwords? (yes/no) [yes]: * is created. > yes * * @return (Role|string)[] The user roles created: src/Entity/User.php */ created: src/Repository/UserRepository.php public function getRoles(); updated: src/Entity/User.php /** updated: config/packages/security.yaml * Returns the password used to authenticate the user. configured one User Provider in your security.yaml file under the providers key * * This should be the encoded password. On authentication, a plain-text * password will be salted, encoded, and then compared to this value. * * @return string The password use Symfony\Component\Security\Core\User\UserInterface; */ public function getPassword(); /** /** * @ORM\Entity(repositoryClass="App\Repository\UserRepository") * Returns the salt that was originally used to encode the password. */ * class User implements UserInterface { The User class must implement UserInterface * This can return null if the password was not encoded using a salt. * // ... * @return string|null The salt } */ Visual identifier that represents the user (isn’t the username), could be an email for e.g. Only used to display who is currently logged in on the web debug toolbar. public function getSalt(); public function getUsername(): string { return (string) $this->email; } * /** * Returns the username used to authenticate the user. * @return string The username */ public function getUsername() ; Enable the User Class as a User Provider /** # config/packages/security.yaml security: * Removes sensitive data from the user. * providers: * This is important if, at any given point, sensitive information like app_user_provider: * the plain-text password is stored on this object. entity: class: App\Entity\User */ property: email public function eraseCredentials(); }
4. By https://andreiabohner.org Security 4.4 Load users from some resource, reload User data from the session, and some other optional features, like remember me, and impersonation (switch_user). Configured under “providers” key in security.yml User Providers Using a Custom Query to Load the User Entity User Provider loads users from database LDAP User Provider loads users from LDAP server Memory User Provider loads users from configuration file Chain User Provider merges two or more user providers into a new user provider Common for traditional web apps. Users are stored in a database and the user provider uses Doctrine to retrieve them Entity User Provider e.g. you want to find a user by email or username Memory User Provider Useful in app prototypes. Stores all user information in a configuration file, including their passwords implement this interface use Doctrine\ORM\EntityRepository; use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface; providers: users: the class of the entity entity: that represents users class: 'App\Entity\User' property: 'username' the property used to query by. # manager_name: 'customer' (can only query from one field) optional: if you're using multiple Doctrine entity managers, this option defines which one to use // src/Repository/UserRepository.php namespace App\Repository; class UserRepository extends EntityRepository implements UserLoaderInterface { // ... define the logic in this method public function loadUserByUsername($usernameOrEmail) { return $this->createQueryBuilder('u') ->where('u.username = :query OR u.email = :query') ->setParameter('query', $usernameOrEmail) ->getQuery() HEADS UP! remove the ->getOneOrNullResult(); property key from the entity } providers: } backend_users: memory: users: john_admin: { password: '$2y$13$a...', roles: ['ROLE_ADMIN'] } jane_admin: { password: '$2y$13$C...', roles: ['ROLE_ADMIN', 'ROLE_SUPER_ADMIN'] } provider in security.yaml LDAP User Provider configure the LDAP client providers: # config/services.yaml in services.yaml my_ldap: services: ldap: Symfony\Component\Ldap\Ldap: service: Symfony\Component\Ldap\Ldap arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter'] base_dn: dc=example,dc=com Symfony\Component\Ldap\Adapter\ExtLdap\Adapter: search_dn: "cn=read-only-admin,dc=example,dc=com" arguments: search_password: password - host: my-server default_roles: ROLE_USER port: 389 uid_key: uid encryption: tls $ composer require symfony/ldap Chain User Provider providers: backend_users: memory: # ... install Combines two or more user providers (entity, memory, and LDAP) to create a new user provider legacy_users: entity: # ... users: entity: # ... all_users: chain: providers: ['legacy_users', 'users', 'backend'] options: protocol_version: 3 referrals: false How Users are Refreshed from Session End of every request User object is serialized to the session Beginning of the next request User object it's deserialized & passed to the user provider to “refresh" it (e.g. Doctrine queries the DB for By default, the core AbstractToken class compares the return values of the: getPassword() getSalt() getUsername() a fresh user). Then, the original User object from the session and the refreshed User object are "compared" to see if they are "equal". If any of these are different, your user will be logged out.
5. By https://andreiabohner.org Security 4.4 Custom User Provider if you're loading users from a custom location (e.g. legacy database connection), you'll need to create a custom user provider // src/Security/UserProvider.php namespace App\Security; use use use use Symfony\Component\Security\Core\Exception\UnsupportedUserException; Symfony\Component\Security\Core\Exception\UsernameNotFoundException; Symfony\Component\Security\Core\User\UserInterface; Symfony\Component\Security\Core\User\UserProviderInterface; Enable the Custom User Provider # config/packages/security.yaml class UserProvider implements UserProviderInterface security: the name of your { user provider can providers: /** be anything * Symfony calls this method if you use features like switch_user your_custom_user_provider: * or remember_me. id: App\Security\UserProvider * * If you're not using these features, you don’t need to implement * this method. * * @return UserInterface * * @throws UsernameNotFoundException if the user is not found */ public function loadUserByUsername($username) { // Load a User object from your data source or throw UsernameNotFoundException. // The $username argument may not actually be a username: // it is whatever value is being returned by the getUsername() method in your User class. throw new \Exception('TODO: fill in loadUserByUsername() inside '.__FILE__); } /** * Refreshes the user after being reloaded from the session. * * When a user is logged in, at the beginning of each request, the * User object is loaded from the session and then this method is * called. Your job is to make sure the user's data is still fresh by, * for example, re-querying for fresh User data. * * If your firewall is "stateless: true" (for a pure API), this method is not called. * * @return UserInterface */ public function refreshUser(UserInterface $user) { if (!$user instanceof User) { throw new UnsupportedUserException(sprintf('Invalid user class "%s".', get_class($user))); } // Return a User object after sure its data is "fresh" or throw a UsernameNotFoundException if user no longer exists throw new \Exception('TODO: fill in refreshUser() inside '.__FILE__); } public function supportsClass($class) { return User::class === $class; } } tells Symfony to use this provider for this User class
6. By https://andreiabohner.org Security Use the UserPasswordEncoderInterface service to encode and check passwords. E.g. using fixtures: 4.4 Encoding Passwords you can control how passwords are encoded in security.yaml Defines the algorithm used to encode passwords. // src/DataFixtures/UserFixtures.php use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; class UserFixtures extends Fixture { private $passwordEncoder; If your app defines more than one user class, each of them can define its own encoding algorithm. public function __construct(UserPasswordEncoderInterface $passwordEncoder) { security: $this->passwordEncoder = $passwordEncoder; your user class name # ... } encoders: bcrypt or sodium are recommended. sodium algorithm: bcrypt is more secure, but requires PHP 7.2 or cost: 12 the Sodium extension App\Entity\User: public function load(ObjectManager $manager) { $user = new User(); $user->setPassword($this->passwordEncoder->encodePassword( $user, security: 'the_new_password' # ... )); recommended: encoders: App\Entity\User: algorithm: auto will use the best algorithm available on your system } } Manually Encode a Password $ php bin/console security:encode-password Authenticating Users Instead of building a route & controller to handle login, you'll activate an authentication provider: some code that runs automatically before your controller is called. Authentication Providers form_login At the beginning of every request, Symfony calls a set of "authentication listeners", or "authenticators" http_basic LDAP via HTTP Basic or Form Login json_login X.509 Client Certificate Authentication (x509) REMOTE_USER Based Authentication (remote_user) simple_form simple_pre_auth Guard Authenticator recommended: allows you to control every part of the authentication process If your application logs users in via a third-party service such as Google, Facebook or Twitter (social login), check out the HWIOAuthBundle community bundle. Comparing Users Manually with EquatableInterface If you need more control over the "compare users" process, make your User class implement EquatableInterface. Then, your isEqualTo() method will be called when comparing users.
7. By https://andreiabohner.org Security Guard Authentication Provider 4.4 $ php bin/console make:auth Create a Login Form Authenticator create an authenticator namespace App\Security; ... Enable the Authenticator class LoginFormAuthenticator extends AbtractFormLoginAuthenticator { You can use AbstractGuardAuthenticator instead to create an API authenticator class used when you choose “Login form authenticator” on make:auth command # config/packages/security.yaml When activated, at the beginning firewalls: of every request, the supports() main: RouterInterface $router, CsrfTokenManagerInterface $csrfTokenManager, method of the authenticator will be called guard: UserPasswordEncoderInterface $passwordEncoder) authenticators: public function __construct(UserRepository $userRepository, { - App\Security\LoginFormAuthenticator ... $this->csrfTokenManager = $csrfTokenManager; } if return: false - nothing else happens. It doesn't call any other methods on the authenticator true - call getCredentials() public function supports(Request $request) { // do your work when we're POSTing to the login page return $request->attributes->get('_route') === 'login' && $request->isMethod('POST'); } read the authentication credentials of the request and return them. Call getUser() and pass this array back to us as the first $credentials argument: public function getCredentials(Request $request) { CSRF Protection return [ 'email' => $request->request->get('email'), templates/security/login.html.twig 'password' => $request->request->get('password'), ... <input type="hidden" name="_csrf_token" 'csrf_token' => $request->request->get('_csrf_token'), ]; value="{{ csrf_token('authenticate') }}"> ... } public function getUser($credentials, UserProviderInterface $userProvider) { $token = new CsrfToken('authenticate', $credentials['csrf_token']); if (!$this->csrfTokenManager->isTokenValid($token)) { throw new InvalidCsrfTokenException(); } Use the $credentials to return a User object, or null if the user isn't found. if return: null - the authentication process stop, and the user will see an error. User object - calls checkCredentials(), and passes to it the same $credentials and User object return $this->userRepository->findOneBy(['email' => $credentials['email']]); } check to see if the user's password is correct, or any other security checks. public function checkCredentials($credentials, UserInterface $user) { } if return: false - authentication would fail and the user see an "Invalid Credentials" message. return $this->passwordEncoder->isPasswordValid($user, $credentials['password']); true - authentication is successful, calls onAuthenticationSuccess() public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) { where to redirect after a successful login if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) { return new RedirectResponse($targetPath); } if there is a referer, redirect to it, if not, to homepage return new RedirectResponse($this->router->generate('homepage')); } protected function getLoginUrl() { return $this->router->generate('login'); } if return: - Response object: will be immediately sent back to the user - nothing: the request would continue to the controller on failure, the authenticator class calls getLoginUrl() and try to redirect here
8. By https://andreiabohner.org Security Guard Authentication Provider 4.4 Guard Authenticator Methods supports(Request $request) getCredentials(Request $request) getUser($credentials, UserProviderInterface $userProvider) checkCredentials($credentials, UserInterface $user) onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) onAuthenticationFailure(Request $request, AuthenticationException $exception) start(Request $request, AuthenticationException $authException = null) supportsRememberMe() you don’t need to handle these 3 methods when using AbtractFormLoginAuthenticator. They are handled automatically Login and Logout Methods // src/Controller/SecurityController.php namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; class SecurityController extends AbstractController { /** * @Route("/login", name="login") the route name compared in supports method of LoginFormAuthenticator class */ public function login(AuthenticationUtils $authenticationUtils) { $error = $authenticationUtils->getLastAuthenticationError(); $lastUsername = $authenticationUtils->getLastUsername(); return $this->render('security/login.html.twig', [ get the login error if there is one last username entered by the user 'last_username' => $lastUsername, 'error' => $error, ]); } /** * @Route("/logout", name="logout") */ just write the method and add the path defined in security.yaml Symfony will automatically log the user out and then redirect them public function logout() { } } Control What Happens After Logout Display Login Error Messages in Templates # config/packages/security.yaml security: templates/security/login.html.twig firewalls: main: logout: {% if error %} point it to a service id of a class that implements LogoutSuccessHandlerInterface path: logout success_handler: logout_success_handler {{ error.messageKey|trans(error.messageData, 'security') }} {% endif %}
9. By https://andreiabohner.org Security Enable it 4.4 # config/packages/security.yaml security: firewalls: Remember Me This is the special name that Symfony uses main: remember_me: secret: <input type="checkbox" name="_remember_me"> Remember me '%kernel.secret%' lifetime: 2592000 # 30 days in seconds Impersonating a User You can go to any URL and add ?_switch_user= and the user identifier (e.g. email) of an user that you want to impersonate. http://example.com/somewhere?_switch_user=john@example.com Enable it Find the Original User security: firewalls: main: switch_user: true $token = $this->security->getToken(); Requires you to have the ROLE_ALLOWED_TO_SWITCH } security: role_hierarchy: ROLE_ADMIN: [ROLE_ALLOWED_TO_SWITCH] Knowing when Impersonation is Active if ($token instanceof SwitchUserToken) { $impersonatorUser = $token->getOriginalToken()->getUser(); Switch Back to the Original User ?_switch_user=_exit http://example.com/somewhere?_switch_user=_exit Custom User Checker When we are switched to another user, Symfony gives us a special role called ROLE_PREVIOUS_ADMIN {% if is_granted('ROLE_PREVIOUS_ADMIN') %} <a href="{{ path('homepage', {'_switch_user': '_exit'}) }}">Exit</a> {% endif %} if you need additional checks before and after user authentication namespace App\Security; use use use use use App\Security\User as AppUser; Symfony\Component\Security\Core\Exception\AccountExpiredException; App\Exception\AccountDeletedException; Symfony\Component\Security\Core\User\UserCheckerInterface; Symfony\Component\Security\Core\User\UserInterface; must implement class UserChecker implements UserCheckerInterface UserCheckerInterface { public function checkPreAuth(UserInterface $user) { if (!$user instanceof AppUser) { return; } user is deleted, show a generic Account Not Found message if ($user->isDeleted()) { # config/packages/security.yaml throw new AccountDeletedException(); security: } defined per firewall firewalls: } main: pattern: ^/ public function checkPostAuth(UserInterface $user) user_checker: App\Security\UserChecker { if (!$user instanceof AppUser) { return; } user account is expired, the user may be notified if ($user->isExpired()) { Enable it throw new AccountExpiredException('...'); } } }
10. By https://andreiabohner.org Security Authorization 4.4 Decide if a user can access some resource This decision will be made by an instance of AccessDecisionManagerInterface The Authorization Process Consists of: 1. Add roles: user receives a specific set of roles when logging in (e.g. ROLE_ADMIN) 2. Check permissions: a resource (e.g. URL, controller) requires a specific role (like ROLE_ADMIN) to be accessed 1. ROLES (define what the user can access) When a user logs in, the getRoles() method on your User object is called to determine which roles the user has Are strings used to grant access to users (e.g. "edit a blog post", "create an invoice"). You can freely choose those strings. The only requirement is that they must start with ROLE_ (e.g. ROLE_POST_EDIT, ROLE_INVOICE_CREATE). you have to return at least one role (e.g. ROLE_USER) for the user Standard ROLES ROLE_USER ROLE_PREVIOUS_ADMIN Added when we are switched to another user ROLE_ADMIN ROLE_ALLOWED_TO_SWITCH Allow switch to another user ROLE_SUPER_ADMIN ROLE_YOUR_DEFINED_NAME Add to all logged users Special “ROLES” you can use these anywhere roles are used: like access_control, controller or in Twig. you can use these roles to check if a user is logged in IS_AUTHENTICATED_REMEMBERED All logged in users have this. Even if you don't use the remember me functionality, you can use this to check if the user is logged in IS_AUTHENTICATED_FULLY Users who are logged in only because of a "remember me" have IS_AUTHENTICATED_REMEMBERED but not have IS_AUTHENTICATED_FULLY IS_AUTHENTICATED_ANONYMOUSLY All users (even anonymous ones) have this Calls the "voter" system Symfony takes the responses from all voters and makes the final decision (allow or deny access to the resource) according to the strategy defined (affirmative, consensus or unanimous 2. Checking Permissions (handle authorization) - for protecting broad URL patterns, use access_control in security.yaml - whenever possible, use the @Security annotation in your controller - check security directly on the security.authorization_checker service (isGranted) for complex situations - define a custom security voter to implement fine-grained restrictions Checking Permissions in the Controller You can use: - annotations (@Security or @IsGranted) - methods (denyAccessUnlessGranted() or isGranted()) Using @Security Annotation use App\Entity\Post; Using @IsGranted Annotation use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; /** /** * @IsGranted("ROLE_ADMIN") * @Security("is_granted('ROLE_ADMIN')") */ public function new() require ROLE_ADMIN for *every* method in this class */ class AdminController extends AbstractController { { /** } * @IsGranted("ROLE_ADMIN") */ /** public function adminDashboard() * @Security("user.getEmail() == post.getAuthorEmail()") { */ public function edit(Post $post) { } } } require ROLE_ADMIN for only this method
11. By https://andreiabohner.org Security 4.4 Using isGranted() and denyAccessUnlesssGranted() Methods Equivalent code without using the "denyAccessUnlessGranted()" shortcut: if (!$post->isAuthor($this->getUser())) { $this->denyAccessUnlessGranted('edit', $post); } use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface ... If access is not granted, a AccessDeniedException is thrown: public function __construct(AuthorizationCheckerInterface $authorizationChecker) { $this->authorizationChecker = $authorizationChecker; - not logged: redirect to the login page - logged in: show the 403 access denied page } ... $this->denyAccessUnlessGranted('ROLE_ADMIN'); if (!$this->authorizationChecker->isGranted('edit', $post)) { $hasAccess = $this->isGranted('ROLE_ADMIN'); throw $this->createAccessDeniedException(); } Checking Permissions in Templates (Twig) {% if is_granted('ROLE_USER') %} .... {% else %} Get the User Who is Logged In Controller Template (Twig) $user = $this->getUser(); {{ app.user.firstName }} <a href="{{ path('app_login') }}">Login</a> Service {% endif %} use Symfony\Component\Security\Core\Security; Access Decision Manager deciding whether or not a user is authorized to perform a certain action class SomeService { private $security; public function __construct(Security $security) Depends on multiple voters, and makes a final verdict based on all the votes (either positive, negative or neutral) it has received. It recognizes several strategies: { $this->security = $security; } affirmative (default) grant access as soon as there is one voter public function someMethod(): string granting access consensus { grant access if there are more voters granting $user = $this->security->getUser(); access than there are denying unanimous only grant access if none of the voters has denied access } } use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; $voters = [...]; instances of Symfony\Component\Security\Core\Authorization\Voter\VoterInterface $strategy = ...; one of "affirmative", "consensus", "unanimous” $allowIfAllAbstainDecisions = ...; whether or not to grant access when all voters abstain $allowIfEqualGrantedDeniedDecisions = ...; whether or not to grant access when there is no majority (only to the "consensus" strategy) $accessDecisionManager = new AccessDecisionManager( $voters, $strategy, $allowIfAllAbstainDecisions, $allowIfEqualGrantedDeniedDecisions ); Change the Default Strategy # config/packages/security.yaml security: access_decision_manager: strategy: unanimous allow_if_all_abstain: false
12. By https://andreiabohner.org Security Are the most granular way of checking permissions 4.4 Voters Creating a Custom Voter When your security logic is complex use custom voters namespace App\Security; use use use use use App\Entity\Post; Symfony\Component\Security\Core\Authentication\Token\TokenInterface; Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; Symfony\Component\Security\Core\Authorization\Voter\Voter; Symfony\Component\Security\Core\User\UserInterface; class PostVoter extends Voter { const CREATE = 'create'; const EDIT = 'edit'; private $decisionManager; private $security; or implement VoterInterface In the Http component, an AccessListener checks access using this manager based on the configured access_control rules public function __construct(AccessDecisionManagerInterface $decisionManager, Security $security) { $this->decisionManager = $decisionManager; $this->security = $security; When isGranted() or denyAccessUnlessGranted() is called, } the first argument is passed here as $attribute (e.g. ROLE_USER, edit) and the second argument (if any) is passed as $subject (e.g. null, a Post object) protected function supports($attribute, $subject) { if (!in_array($attribute, [self::CREATE, self::EDIT])) { return false; } if (!$subject instanceof Post) { return false; } return true; If return false this voter is done: some other voter should process this Using the Custom Voter you can use the voter with the @Security annotation: If return true, voteOnAttribute() will be called } /** * @Security("is_granted('edit', post)") */ public function edit(Post $post) { // ... } protected function voteOnAttribute($attribute, $subject, TokenInterface $token) { The $token can be used to find $user = $token->getUser(); the current user object (if any) if (!$user instanceof UserInterface) { return false; the user must be logged in; if not, deny access } You can also use this directly with the security.authorization_checker service or via the even easier shortcut in a controller: if ($this->security->isGranted('ROLE_SUPER_ADMIN')) { return true; } Checking for Roles inside a Voter /** @var Post $post */ $post = $subject; you know $subject is a Post object, thanks to supports method switch ($attribute) { case self::CREATE: if ($this->decisionManager->decide($token, ['ROLE_ADMIN'])) { return true; if the user is an admin, } allow them to create new posts break; case self::EDIT: if ($user->getEmail() === $post->getAuthorEmail()) { return true; if the user is the author of } the post, allow them to edit the posts break; } return false; } } return: true - to allow access false - to deny access /** * @Route("/{id}/edit", name="admin_post_edit") */ public function edit($id) { $post = ...; // query for the post $this->denyAccessUnlessGranted('edit', $post); }
13. By https://andreiabohner.org Security 4.4 Where the security system is configured SecurityBundle Configuration (security.yaml) # config/packages/security.yaml security: where user is redirected after a 403 HTTP error (unless there is a custom access deny handler) access_denied_url: null always_authenticate_before_granting: false if true: eraseCredentials() method of the user object is called after authentication hide_user_not_found: true if true: when a user isn’t found a generic BadCredentialsException exception is thrown w/ msg "Bad credentials" if false: UsernameNotFoundException exception is thrown and includes the given not found username session_fixation_strategy: migrate user providers app_user_provider: entity: class: App\Entity\User property: email encoders: password encoders App\Entity\User: algorithm: auto App\Entity\User: 'bcrypt' use the best possible algorithm available on your system bcrypt encoder with default options App\Entity\User: algorithm: 'bcrypt' cost: App\Entity\User: Password Encoders bcrypt encoder with custom options 15 App\Entity\User: 'sodium' sodium encoder with default options 'sodium' sodium encoder with custom options memory_cost: 16384 Amount in KiB (16384=16 MiB) time_cost: 2 Number of iterations threads: 4 Number of parallel threads algorithm: NONE: session isn’t changed MIGRATE: session id is updated, attributes are kept INVALIDATE: session id is updated, attributes are lost protection against session fixation. Possible values: providers: users: entity: class: 'App\Entity\User' property: 'username' # manager_name: 'customer' my_ldap: ldap: service: Symfony\Component\Ldap\Ldap base_dn: dc=example,dc=com search_dn: "cn=read-only-admin,dc=example,dc=com" ... backend_users: memory: users: user: {password: userpass, roles: ['ROLE_USER']} admin: {password: adminpass, roles: ['ROLE_ADMIN']} custom_user_provider: id: App\Security\UserProvider all_users: chain: providers: ['my_ldap', 'users', 'backend'] User Providers erase_credentials: true providers: if true: user is asked to authenticate before each call to the isGranted() in services, controllers, or templates Different Password Encoder for Each User // src/Acme/UserBundle/Entity/User.php App\Entity\User: algorithm: argon2i argon2i encoder with custom options namespace Acme\UserBundle\Entity; memory_cost: 256 use Symfony\Component\Security\Core\Encoder\EncoderAwareInterface; time_cost: 1 use Symfony\Component\Security\Core\User\UserInterface; PBKDF2 encoder using SHA512 hashing with default options threads: 2 App\Entity\User: 'sha512' app_encoder: custom named encoder: create your own password encoders as services class User implements UserInterface, EncoderAwareInterface { public function getEncoderName() { if ($this->isAdmin()) { id: 'App\Security\Encoder\MyCustomPasswordEncoder’ return 'extra_secure'; use the ‘extra_secure’ encoder only for admin users } extra_secure: algorithm: sodium memory_cost: 16384 time_cost: 2 threads: 4 return null; } } use the default encoder
14. By https://andreiabohner.org Security 4.4 Firewalls are listeners of the HTTP component that defines the authentication mechanism used for each URL (or URL pattern) of your app all firewalls are one AuthenticationProviderManager (and thus, one security system) firewalls: main: name of the firewall name of the firewall (can be chosen freely) # ... impersonating users can be done by activating the switch_user firewall listener switch_user: true # switch_user: Authentication main: # role: ROLE_ADMIN # parameter: _change_user your web server is doing all the authentication process itself x509: provider: your_user_provider remote_user: allow change the ROLE and query string used provider: your_user_provider simple_preauth: # ... # AnonymousAuthenticationProvider allow anonymous requests so anonymous: true users can access public pages multiple guard authenticators using shared (one) entry point guard: authenticators: - App\Security\LoginFormAuthenticator # Use UsernamePasswordToken & DaoAuthenticationProvider - App\Security\FacebookConnectAuthenticator form_login: true entry_point: App\Security\LoginFormAuthenticator logout: form_login: path: app_logout where to redirect after logout target: app_any_route login_path: /login handles a login form POST automatically check_path: /login_check success_handler: logout_success_handler csrf_token_generator: security.csrf.token_manager csrf_parameter: _csrf_token csrf_token_id: a_private_string default: one year secret: '%kernel.secret%' lifetime: 604800 # 1 week in seconds path: / domain: null secure: false if set to ‘strict’, the cookie will not be sent with cross-site requests httponly: true samesite: null remember_me_parameter: _remember_me catch_exceptions: false Authentication Providers remember_me: default_target_path: /after_login_route_name always_use_default_target_path: false use_referer: false failure_path: login_failure_route_name target_path_parameter: _target_path failure_path_parameter: back_to username_parameter: _username password_parameter: _password post_only: true name of the password field if true: user will be forwarded use_forward: false to the login form instead of form_login_ldap: redirected token_provider: token_provider_id #always_remember_me: true name of the username field always enable remember me service: Symfony\Component\Ldap\Ldap dn_string: 'uid={username},dc=example,dc=com' stateless: false json_login: enable custom user checker check_path: login username_path: security.credentials.login user_checker: App\Security\UserChecker password_path: security.credentials.password simple_form: # ... Restrict Firewalls to a Request http_basic: asks credentials (username & password) using a dialog in the browser. You cannot use logout with http_basic realm: Secured Area security: firewalls: http_basic_ldap: name of the firewall service: Symfony\Component\Ldap\Ldap secured_area: pattern: ^/admin dn_string: 'uid={username},dc=example,dc=com' by path host: ^admin\.example\.com$ methods: [GET, POST] http_digest: by host # ... by HTTP methods request_matcher: app.firewall.secured_area.request_matcher 'pattern' is a regexp matched against the request URL. If there's a match, authentication is triggered by service name of the firewall api: pattern: ^/api/ guard: multiple guard authenticators using separate entry points (firewall) authenticators: - App\Security\ApiTokenAuthenticator
15. By https://andreiabohner.org Security 4.4 Only one path will be matched per request: Symfony starts at the top of the list and as soon as it finds one access control that matches the URL, it uses that and stops. The order of paths is important! Each access_control can also match on IP address, hostname and HTTP methods. It can also be used to redirect a user to the https version of a URL pattern access_control: Matching Options can be: - path - ip or ips (netmasks are supported) - port - host - methods - { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/internal, roles: IS_AUTHENTICATED_ANONYMOUSLY, ips : [127.0.0.1, ::1, 192.168.0.1/24] } Authorization - { path: ^/internal, roles: ROLE_NO_ACCESS } ‘ips' option supports IP addresses and subnet masks - path: ^/_internal/secure allow_if: "'127.0.0.1' == request.getClientIp() or is_granted('ROLE_ADMIN')” using an expression # matches /admin/users/* - { path: ^/admin/users, roles: ROLE_SUPER_ADMIN } # matches /admin/* except for anything matching the above rule - { path: ^/admin, roles: ROLE_ADMIN } - { path: ^/profile, roles: ROLE_USER } - { path: ^/admin, roles: ROLE_USER_IP, ip : 127.0.0.1 } - { path: ^/admin, roles: ROLE_USER_PORT, ip : 127.0.0.1, port : 8080 } - { path: ^/admin, roles: ROLE_USER_HOST, host : symfony\.com$ } - { path: ^/admin, roles: ROLE_USER_METHOD, methods : [POST, PUT] } - { path: ^/admin, roles: ROLE_USER } - { path: ^/cart/checkout, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https } Instead of giving many roles to each user, you can define role inheritance rules by creating a role hierarchy role_hierarchy: ROLE_ADMIN: force redirect to HTTPs ROLE_USER ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] access_decision_manager: strategy: unanimous change the default access decision strategy (decide whether or not a user is authorized to perform a certain action using voters) allow_if_all_abstain: false # displays the default config values defined by Symfony $ php bin/console config:dump-reference security Console # displays the actual config values used by your app $ php bin/console debug:config security Standard Voters AuthenticatedVoter checks if the token is fully authenticated, anonymous, ... votes if IS_AUTHENTICATED_FULLY, IS_AUTHENTICATED_REMEMBERED, or IS_AUTHENTICATED_ANONYMOUSLY is present. ExpressionVoter votes based on the evaluation of an expression created with the ExpressionLanguage component RoleVoter votes if any attribute starts with a given prefix. (supports attributes starting with ROLE_ and grants access to the user when the required ROLE_* attributes can all be found in the array of roles returned by the token's getRoleNames() method) RoleHierarchyVoter understands hierarchies in roles (e.g. “admin is a user”). Extends RoleVoter and uses a RoleHierarchy to determine the roles granted to the user before voting

Home - Wiki
Copyright © 2011-2024 iteam. Current version is 2.139.0. UTC+08:00, 2024-12-23 05:58
浙ICP备14020137号-1 $Map of visitor$