ivan tsai
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Security Symfony 的安全系統非常強大,但設置起來也很混亂。在本文件中,將學習如何逐步設置應用的安全系統: ## 1. 安裝 在使用Symfony Flex的應用程式中,執行此命令以安裝安全功能: ``` composer require symfony/security-bundle ``` ## 2a) 創建你的user class 無論如何進行身份驗證(例如登錄表單或 API toekn)或您的user資料將存儲在何處(資料庫、單點登錄),下一步都是相同的:創建一個“user”class。最簡單的方法是使用```MakerBundle```。 假設您想使用 Doctrine 將user資料存儲在資料庫中: ``` php bin/console make:user The name of the security user class (e.g. User) [User]: > User Do you want to store user data in the database (via Doctrine)? (yes/no) [yes]: > yes Enter a property name that will be the unique "display" name for the user (e.g. email, username, uuid [email] > email Does this app need to hash/check user passwords? (yes/no) [yes]: > yes created: src/Entity/User.php created: src/Repository/UserRepository.php updated: src/Entity/User.php updated: config/packages/security.yaml ``` 該命令會詢問幾個問題,以便它可以準確地生成您需要的內容。最重要的是```User.php```文件本身。 關於您的```User```class的唯一規則,是它必須實現UserInterface。 隨意添加您需要的任何其他fields或邏輯。 如果您的User class是一個entity(如本範例中所示),您可以使用```make:entity```添加更多field。 此外,請確保為有為你的新entity進行migration: ``` php bin/console make:migration php bin/console doctrine:migrations:migrate ``` ## 2b) “user provider” 除了你的```User``` class,你還需要一個“user提供者”:一個可以幫助做一些事情的class,比如從session中重新load user資料和一些可選功能,比如"記住我([remember me](https://symfony.com/doc/4.3/security/remember_me.html))"和"模擬([impersonation](https://symfony.com/doc/4.3/security/impersonating_user.html))。 幸運的是,```make:user```命令已經在```security.yaml```文件中為您配置了一個 providers key。 如果你的```User``` class是一個entity,你不需要做任何其他事情。 但是如果你的class不是entity,那麼```make:user```也會生成一個 ```UserProvider``` class。 在此處了解有關[user提供程式](https://symfony.com/doc/4.3/security/user_provider.html)的更多訊息。 ## 2c) encoding 密碼 並非所有應用程式都有需要密碼的“user”。 如果您的user有密碼,您可以透過```security.yaml```控制這些密碼的編碼方式。 不然```make:user``` 命令將為您預先配置: ``` # config/packages/security.yaml security: # ... encoders: # use your user class name here App\Entity\User: # Use native password encoder # This value auto-selects the best possible hashing algorithm # (i.e. Sodium when available). algorithm: auto ``` 現在,symfony知道如何要encode的密碼,在把user資料存在資料庫之前,你可以使用 ```UserPasswordEncoderInterface```服務達到這點 例如,通過使用```DoctrineFixturesBundle```,可以創建虛擬資料庫user: ``` php bin/console make:fixtures The class name of the fixtures to create (e.g. AppFixtures): > UserFixtures ``` 使用此服務對密碼進行encode: ```php= // src/DataFixtures/UserFixtures.php + use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; // ... class UserFixtures extends Fixture { + private $passwordEncoder; + public function __construct(UserPasswordEncoderInterface $passwordEncoder) + { + $this->passwordEncoder = $passwordEncoder; + } public function load(ObjectManager $manager) { $user = new User(); // ... + $user->setPassword($this->passwordEncoder->encodePassword( + $user, + 'the_new_password' + )); // ... } } ``` 也可以通過手動執行來encode密碼: ``` php bin/console security:encode-password ``` ## 3a) 身份驗證和防火牆 安全系統配置在```config/packages/security.yaml```.是防火牆(firewalls)重要的部分: ``` # config/packages/security.yaml security: firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: anonymous: ~ ``` “防火牆”是您的身份驗證系統:它下面的配置定義了您的user將如何進行身份驗證(例如登錄表單、API token等)。 每個請求只有一個防火牆處於活動狀態:Symfony 使用pattern key來查找第一個match項(您也可以通過host或其他方式進行match)。 ```dev```防火牆是假的防火牆:它只是確保你不小心阻擋的Symfony的開發工具-像```URL/_profiler``` 和```/_wdt```。 所有真實的URL都由```main```防火牆處理(沒有pattern key意味著它match所有URL)。 但這並不意味著每一個URL需要驗證。多虧了```anonymous``` key,該防火牆是匿名訪問。 事實上,如果現在轉到主頁,您將擁有訪問權限,並且您會看到您已“通過身份驗證”為```anon..``` 不要被 ```Authenticated``` 旁邊的“Yes”所迷惑。 防火牆驗證它不知道您的身份,因此,您是匿名的: ![](https://i.imgur.com/6JEQpXT.png) 稍後將學習如何拒絕對某些 URL 或控制器的訪問。 >note >如果您沒有看到工具欄,請使用以下命令安裝分析器: >``` >composer require --dev symfony/profiler-pack >``` 現在我們了解了我們的防火牆,下一步是為您的user創建一種驗證方式! ## 3b) 驗證您的用戶 Symfony 中的身份驗證起初可能會讓人覺得有點“神奇”。 這是因為,您將啟用一個身份驗證提供(authentication provider)程式,而不是構建一個路由和控制器來處理登錄 :一些在呼叫您的控制器之前自動運行的code。 Symfony 有幾個內劍的身份驗證提供程式。 如果您的用例完全match其中之一,可以省下一些麻煩! 但是,在大多數情況下 - 包括登錄表單 -我們建議構建一個 *Guard Authenticator*:一個允許您控制身份驗證過程的每個部分的class(請參閱下一節)。 :::success 如果您的應用程式透過第三方服務(例如 Google、Facebook 或 Twitter(社交登錄))讓user登錄,請查看[HWIOAuthBundle](https://github.com/hwi/HWIOAuthBundle)bundle。 ::: ### 保護驗證器 Guard 身份驗證器是一個class,可讓您完全控制身份驗證過程。有許多不同的方法可以構建身份驗證器,因此這裡有一些常見的範例: - [如何構建登錄表單](https://symfony.com/doc/4.3/security/form_login_setup.html) - [帶有 Guard 的自定義身份驗證系統(API token 範例)](https://symfony.com/doc/4.3/security/guard_authentication.html) ## 4) 拒絕存取(access)、角色(roles)和其他授權(Authorization) user現在可以使用您的登錄表單登錄您的應用程式。 現在,您需要學習如何拒絕存取並使用 User ibject。 這稱為授權,其工作是決定user是否可以訪問某些資源(URL、模型object、呼叫方法(method call)等)。 授權過程有兩個不同的方面: 1. 用戶在登錄時會收到一組特定的角色(例如ROLE_ADMIN)。 2. 手動添加code,以便資源(例如 URL、控制器)需要特定的“屬性”(最常見的是類似 的角色ROLE_ADMIN)才能被訪問。 ### 角色 當user登錄時,Symfony 會呼叫```getRoles()```您對User object上的方法來確定該user具有哪些角色。 在稍早我們生成的```User``` class中,角色(role)是一個存儲在資料庫中的array,每個user總是至少被賦予一個角色```ROLE_USER:```: ```php= // src/Entity/User.php // ... /** * @ORM\Column(type="json") */ private $roles = []; public function getRoles(): array { $roles = $this->roles; // guarantee every user at least has ROLE_USER $roles[] = 'ROLE_USER'; return array_unique($roles); } ``` 這是一個很好的預設配置,但是你可以做任何你想做的事情來確定用戶應該擁有哪些角色。以下是一些原則: - 每個角色都必須以 ```ROLE_```開始(否則,事情不會按預期工作) - 除了上面的規則,角色只是一個字串,你可以創造你需要的東西(例如```ROLE_PRODUCT_ADMIN```)。 接下來,您將使用這些角色授予對你的網站特定部分的訪問權限。 您還可以使用[角色層次結構(role hierarchy)](https://symfony.com/doc/4.3/security.html#security-role-hierarchy),其中某些角色會自動為您提供其他角色。 ### 添加code以拒絕存取 有兩種方法可以拒絕存取某些內容: 1. [```access_control``` 中的 ```security.yaml```](https://symfony.com/doc/4.3/security.html#security-authorization-access-control) 允許您保護 URL 模式(例如```/admin/*```)。更簡單,但不靈活; 2. [在您的控制器(或其他code)中。](https://symfony.com/doc/4.3/security.html#security-securing-controller) #### 保護 URL 模式 (access_control) 對於應用程式的安全性而言,最基本方法是保護```security.yaml```中的URL模式 例如,要求所有URLs中的```ROLE_ADMIN```以開頭的```/admin```,您可以: ``` # config/packages/security.yaml security: # ... firewalls: # ... main: # ... access_control: # require ROLE_ADMIN for /admin* - { path: '^/admin', roles: ROLE_ADMIN } # or require ROLE_ADMIN or IS_AUTHENTICATED_FULLY for /admin* - { path: '^/admin', roles: [IS_AUTHENTICATED_FULLY, ROLE_ADMIN] } # the 'path' value can be any valid regular expression # (this one will match URLs like /api/post/7298 and /api/comment/528491) - { path: ^/api/(post|comment)/\d+$, roles: ROLE_USER } ``` 可以根據需求,定義任意數量的 URL 模式 - 每個都是一個正則表達式。 但是,每個request只會match一個:從Symfony 列表的頂部開始,並在找到第一個match項時停止: ``` # config/packages/security.yaml security: # ... access_control: # matches /admin/users/* - { path: '^/admin/users', roles: ROLE_SUPER_ADMIN } # matches /admin/* except for anything matching the above rule - { path: '^/admin', roles: ROLE_ADMIN } ``` 將路徑放在```^```前面意味著只match以該模式開頭的URL。 例如,```/admin```(沒有```^```)的路徑會match成功,但```/admin/foo```也會match成功 每個```access_control```還可以match IP 地址、host name和 HTTP 方法。 它還可用於將user重新導向到https版本的URL模式。 請參閱[access_control 如何工作.](https://symfony.com/doc/4.3/security/access_control.html) #### 保護控制器和其他code 可以從控制器內部拒絕存取: ```php= // src/Controller/AdminController.php // ... public function adminDashboard() { $this->denyAccessUnlessGranted('ROLE_ADMIN'); // or add an optional message - seen by developers $this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'User tried to access a page without having ROLE_ADMIN'); } ``` 如果未授予存取權限, 則會拋出一個特殊的```AccessDeniedException```並且不再執行控制器中的code。 然後,將發生以下兩種情況之一: - 如果user還沒有登錄,他們將被要求登錄(例如重新導向到登錄頁面)。 - 如果user正在登錄,但不會有```ROLE_ADMIN```的身分,他們將被示以403拒絕訪問頁面(可以[自定義](https://symfony.com/doc/4.3/controller/error_pages.html#controller-error-pages-by-status-code)拒絕存取的頁面)。 透過```SensioFrameworkExtraBundle```,您還可以使用註釋保護您的控制器: ```php= // src/Controller/AdminController.php // ... + use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; + /** + * Require ROLE_ADMIN for *every* controller method in this class. + * + * @IsGranted("ROLE_ADMIN") + */ class AdminController extends AbstractController { + /** + * Require ROLE_ADMIN for only this controller method. + * + * @IsGranted("ROLE_ADMIN") + */ public function adminDashboard() { // ... } } ``` 有關更多資訊,請參閱[FrameworkExtraBundle](https://symfony.com/bundles/SensioFrameworkExtraBundle/current/index.html)。 #### 模板中的存取控制 如果要檢查當前user是否具有某個角色,可以使用```is_granted()```任何 Twig 模板中的內建幫助函數(helper): ```php {% if is_granted('ROLE_ADMIN') %} <a href="...">Delete</a> {% endif %} ``` #### 保護其他服務 請參閱[如何保護應用程式中的任何服務或方法](https://symfony.com/doc/4.3/security/securing_services.html)。 ### 檢查user是否已登錄 (IS_AUTHENTICATED_FULLY) 如果你只是想檢查user登入與否(你不關心角色身分),你有兩個選擇。 首先,如果你給了每個user ```ROLE_USER```,你就可以檢查那個角色。 否則,您可以使用特殊的“屬性”代替角色: ```php= // ... public function adminDashboard() { $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY'); // ... } ``` 您在任何角色上使用```IS_AUTHENTICATED_FULLY```:像 ```access_control```或在 Twig 中。 ```IS_AUTHENTICATED_FULLY```不是一個角色,但它有點像一個角色,每個登錄的user都會有這個。 實際上,有 3 個特殊屬性,如下所示: - ```IS_AUTHENTICATED_REMEMBERED```:所有登錄的user都有這個,即使他們是因為“remeber me 的cookie”而登錄的。 即使您不使用“remember me”功能,您也可以使用它來檢查用戶是否已登錄。 - ```IS_AUTHENTICATED_FULLY```: 這類似於```IS_AUTHENTICATED_REMEMBERED```,但更強。僅因為“remember me cookie”而登錄的user將擁有```IS_AUTHENTICATED_REMEMBERED```但不會擁有```IS_AUTHENTICATED_FULLY```. - IS_AUTHENTICATED_ANONYMOUSLY:所有user(甚至匿名user)都有這個 這在將URL列入白名單以保證存取時很有用- 一些細節在此[access_control 如何工作?](https://symfony.com/doc/4.3/security/access_control.html). ### 存取控制列表 (ACL):保護單個資料庫object 想像一下,您正在設計一個blog,user可以在其中評論您的發文。 您還希望user能夠編輯自己的評論,但不能編輯其他user的評論。 此外,作為管理員(admin)user,您希望能夠編輯所有評論。 [投票者(Voters)](https://symfony.com/doc/4.3/security/voters.html)允許您編寫您需要的任何商業邏輯(例如,user可以編輯此發文,因為他們是創建者)來確定存取權限。 這就是 Symfony官方推薦你創建ACL-like安全系統的原因。 如果您仍然喜歡使用傳統的 ACL,請參閱[symfony ACL bundle](https://github.com/symfony/acl-bundle)。 ## 5a) 獲取user object 認證後,```User```可以通過```getUser()```快速的方式訪問當前user的object: ```php= public function index() { // usually you'll want to make sure the user is authenticated first $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY'); // returns your User object, or null if the user is not authenticated // use inline documentation to tell your editor your exact User class /** @var \App\Entity\User $user */ $user = $this->getUser(); // Call whatever methods you've added to your User class // For example, if you added a getFirstName() method, you can use that. return new Response('Well hi there '.$user->getFirstName()); } ``` ## 5b) 從服務中獲取user 如果您需要從服務中獲取登錄user,請使用[Security服務](https://api.symfony.com/4.3/Symfony/Component/Security/Core/Security.html): ```php= // src/Service/ExampleService.php // ... use Symfony\Component\Security\Core\Security; class ExampleService { private $security; public function __construct(Security $security) { // Avoid calling getUser() in the constructor: auth may not // be complete yet. Instead, store the entire Security object. $this->security = $security; } public function someMethod() { // returns User object or null if not authenticated $user = $this->security->getUser(); } } ``` ### 在模板中獲取user 在 Twig 模板中,因為[twig全域app變量](https://symfony.com/doc/4.3/templates.html#twig-app-variable),所以可以透過```app.user```取得user object ```php= {% if is_granted('IS_AUTHENTICATED_FULLY') %} <p>Email: {{ app.user.email }}</p> {% endif %} ``` ## Logging out 要啟用logging out,請啟用```logout```防火牆下的配置參數: ``` # config/packages/security.yaml security: # ... firewalls: main: # ... logout: path: app_logout # where to redirect after logout # target: app_any_route ``` 接下來,您需要為此 URL 創建路由(但不是控制器): ``` # config/routes.yaml app_logout: path: /logout methods: GET ``` 透過將user發送到```app_logout```路由(即到```/logout```),Symfony 將取消對當前user的身份驗證並將他們重新導向。 :::success 需要更多在logout之後的事情?在```logout```之下添加```success_handler``` key然後指出某個class 的服務id用以實現[LogoutSuccessHandlerInterface](https://api.symfony.com/4.3/Symfony/Component/Security/Http/Logout/LogoutSuccessHandlerInterface.html) 。 ::: ## 層級角色(Hierarchical roles) 您可以通過創建角色層次結構來定義角色繼承規則,而不是為每個user分配多個角色: ``` # config/packages/security.yaml security: # ... role_hierarchy: ROLE_ADMIN: ROLE_USER ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] ``` 擁有```ROLE_ADMIN```角色user也將擁有```ROLE_USER```角色。 而擁有```ROLE_SUPER_ADMIN```, 的user將自動擁有 ```ROLE_ADMIN,ROLE_ALLOWED_TO_SWITCH```和```ROLE_USER```(繼承自```ROLE_ADMIN```)。 要使角色層次結構起作用,請不要嘗試手動呼叫```$user->getRoles()```。例如,在從基本控制器擴展的控制器中: ```php= // BAD - $user->getRoles() will not know about the role hierarchy $hasAccess = in_array('ROLE_ADMIN', $user->getRoles()); // GOOD - use of the normal security methods $hasAccess = $this->isGranted('ROLE_ADMIN'); $this->denyAccessUnlessGranted('ROLE_ADMIN'); ``` >note >這些```role_hierarchy```值是靜態的 - 例如,您不能將角色層次結構存>儲在資料庫中。 > >如果需要,請創建一個客製化的[安全voter](https://symfony.com/doc/4.3/security/voters.html),以在資料庫中查找user的角色。 ## 經常有的疑問 - 我可以有多個防火牆嗎? 是的!但通常沒有必要。每個防火牆就像一個單獨的安全系統。 因此,除非您有非常不同的身份驗證需求,否則一個防火牆通常可以很好地工作。使用Guard身份驗證,您可以在同一防火牆下創建各種不同的身份驗證方式(例如表單登錄、API key身份驗證和 LDAP)。 - 我可以在防火牆之間共享身份驗證嗎? 是的,但只有一些配置。 如果您使用多個防火牆並針對一個防火牆進行身份驗證,則不會自動針對任何其他防火牆進行身份驗證。 不同的防火牆就像不同的安全系統。為此,您必須為不同的防火牆明確指定相同的 安全配置 ([SecurityBundle](https://symfony.com/doc/4.3/reference/configuration/security.html#reference-security-firewall-context))。 但通常對於大多數應用程式來說,擁有一個主防火牆就足夠了。 - 安全性似乎不適用於我的錯誤頁面 由於路由是在配置security之前完成的,因此任何防火牆都不會 404 錯誤頁面覆蓋。 這意味著您無法檢查安全性,甚至無法訪問這些頁面上的user object。有關 更多詳細資訊,請參閱如何[自定義錯誤頁面](https://symfony.com/doc/4.3/controller/error_pages.html)。 - 我的身份驗證似乎沒有作用:沒有錯誤,但我從未登錄 有時,身份驗證可能會成功,但在重新導向後,由於```User```從session load時出現問題,您會立即logout。 要查看這是否是這個問題,請檢查log文件 (```var/log/dev.log```) 中的log訊息: - 無法刷新token,因為user已改變 如果你看到這個,有兩個可能的原因。 首先,從session load您的user可能會出現問題。 請參閱[安全user提供程式](https://symfony.com/doc/4.3/security/user_provider.html#user_session_refresh)。 其次,如果自上次頁面重新整理,導致資料庫中的某些user資料發生了更改,Symfony 會出於安全原因故意logout user。 其他更多資訊可以參考官網的[security最下方](https://symfony.com/doc/4.3/security.html#authentication-identifying-logging-in-the-user)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully