# LIFF 開發框架 [TOC] ###### tags: `chatgpt` --- ## 聽過 LIFF 嗎 是的,我知道 LIFF。LIFF 全名為 Line Front-end Framework,是 Line 的一個開發框架,用於開發 Line Bot 的前端應用程序。 LIFF 提供了一種輕量級的方式來開發 Line Bot 的前端,開發者可以使用 HTML、CSS 和 JavaScript 等前端技術來設計和實現 Line Bot 的介面和功能,並與 Line Bot 後端進行通信。 ## 由於 LIFF 已處理了前端的認證及授權,當呼叫 REST API 時如何向後端進行認證及授權? 當使用 LIFF 框架開發 Line Bot 前端應用程序時,LIFF 已經處理了前端的認證及授權,因此在呼叫 REST API 時,可以使用 LIFF 提供的 token 來向後端進行認證及授權。 在 LIFF 中,可以使用 `liff.getAccessToken()` 方法獲取當前用戶的 access token,然後將 access token 傳遞到後端 API 中,讓後端 API 對用戶進行驗證和授權。 以下是一個簡單的 JavaScript 代碼示例,展示如何使用 LIFF 的 access token 進行 API 認證及授權: ```javascript // 呼叫 LIFF API 獲取 access token const accessToken = await liff.getAccessToken(); // 發送 API 請求,將 access token 作為請求 header 中的 Authorization 欄位 const response = await fetch('https://your-api.com/your-endpoint', { headers: { 'Authorization': `Bearer ${accessToken}` } }); // 處理 API 响應 const data = await response.json(); ``` 在這個示例中,`fetch` 方法被用來發送 API 請求,並將 access token 作為 Authorization 欄位的值傳遞到 API。注意,這裡的 access token 是一個字串,需要使用 Bearer 方案來進行認證。在實際應用中,你需要將 **`https://your-api.com/your-endpoint`** 替換為實際的 API 端點,並確保你的 API 正確處理了 access token。 ## 後端框架為 Spring Boot,並搭配外部的 Keycloak,該怎麼實作呢 如果你的後端框架是 Spring Boot,並且你使用外部的 Keycloak 來進行身份驗證和授權,你可以使用 Spring Security 框架來實現身份驗證和授權。 以下是一個簡單的 Spring Security 配置,展示如何使用 Keycloak 進行身份驗證和授權: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(keycloakAuthenticationProvider()); } @Bean public KeycloakSpringBootConfigResolver keycloakConfigResolver() { return new KeycloakSpringBootConfigResolver(); } @Override protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); } @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http.authorizeRequests() .antMatchers("/api/**").authenticated() .anyRequest().permitAll(); } @Bean @Override protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessingFilter() throws Exception { KeycloakAuthenticationProcessingFilter filter = new KeycloakAuthenticationProcessingFilter(authenticationManagerBean()); filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy()); return filter; } @Bean public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(KeycloakPreAuthActionsFilter filter) { FilterRegistrationBean registration = new FilterRegistrationBean(filter); registration.setEnabled(false); return registration; } @Bean public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(KeycloakAuthenticationProcessingFilter filter) { FilterRegistrationBean registration = new FilterRegistrationBean(filter); registration.setEnabled(false); return registration; } @Bean public FilterRegistrationBean keycloakAuthenticatedActionsFilterBean(KeycloakAuthenticatedActionsFilter filter) { FilterRegistrationBean registration = new FilterRegistrationBean(filter); registration.setEnabled(false); return registration; } @Bean public FilterRegistrationBean keycloakSecurityContextRequestFilterBean(KeycloakSecurityContextRequestFilter filter) { FilterRegistrationBean registration = new FilterRegistrationBean(filter); registration.setEnabled(false); return registration; } @Bean public FilterRegistrationBean keycloakCorsFilterRegistrationBean(KeycloakCorsFilter filter) { FilterRegistrationBean registration = new FilterRegistrationBean(filter); registration.setEnabled(false); return registration; } } ``` 這個配置中,我們繼承了 `KeycloakWebSecurityConfigurerAdapter` 類,這個類提供了 Keycloak 的 Spring Security 配置。我們使用 `authenticationProvider()` 方法來配置 Keycloak 的身份驗證提供者,使用 `keycloakAuthenticationProcessingFilter()` 方法來配置 Keycloak 的身份驗證過濾器。我們還使用 `configure()` 方法來配置 URL 路徑的授權,這裡的 `/api/**` URL 路徑需要進行身份驗證,其他路徑則不需要。 在上面的代碼中,我們還配置了一些過濾器的 Bean,這些過濾器在 ## 後端框架為 Spring Boot,並搭配外部的 Keycloak,該怎麼實作來自 LIFF 的 access token 呢 如果你的後端框架是 Spring Boot,並且你使用外部的 Keycloak 來進行身份驗證和授權,你可以使用 Spring Security 框架來實現 LIFF 傳遞的 access token 的驗證和授權。 以下是一個簡單的 Spring Security 配置,展示如何使用 Keycloak 進行身份驗證和授權,並從 LIFF 傳遞的 access token 中驗證授權: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(keycloakAuthenticationProvider()); } @Bean public KeycloakSpringBootConfigResolver keycloakConfigResolver() { return new KeycloakSpringBootConfigResolver(); } @Override protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); } @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http.authorizeRequests() .antMatchers("/api/**").hasAnyRole("user") .anyRequest().permitAll() .and() .oauth2ResourceServer() .jwt() .decoder(jwtDecoder()); } @Bean public JwtDecoder jwtDecoder() { return JwtDecoders.fromIssuerLocation("<ISSUER_URL>"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider(); keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); auth.authenticationProvider(keycloakAuthenticationProvider); } } ``` 在這個配置中,我們使用 oauth2ResourceServer() 方法來配置 Spring Security 能夠接受來自 LIFF 傳遞的 access token,使用 `jwt()` 方法來指定 JWT 設定。並在 `jwtDecoder()` 方法中指定 JWT 的簽發者 URL。這樣 Spring Security 就可以在接收到 LIFF 傳遞的 access token 後,使用指定的 JWT 簽發者 URL 進行驗證。 另外,我們在 `configure()` 方法中使用 `hasAnyRole("user")` 方法來授予 `/api/**` URL 路徑的 `user` 角色權限,這樣只有具有 `user` 角色的用戶才能訪問這些 URL 路徑。 最後,我們在 `configure(AuthenticationManagerBuilder auth)` 方法中設置了 `KeycloakAuthenticationProvider` 的 `GrantedAuthoritiesMapper`,並使用 `authenticationProvider()` 方法來配置 Keycloak 的身份驗證提供者。這樣就能從 LIFF 傳遞的 access token 中解析出用戶的角色,並將其授予 Spring Security 中對應的角色權限。 ## 那外部 Keycloak 該如何設定以配合前述情境 要設置外部的 Keycloak 以配合 Spring Boot 中使用 LIFF 傳遞的 access token,你可以遵循以下步驟: 1. 創建一個新的 Keycloak 客戶端,用於與 LIFF 應用程序集成。在 Keycloak 中,轉到 Clients -> Create,然後輸入客戶端的詳細信息,例如客戶端 ID 和描述。 2. 配置新創建的 Keycloak 客戶端。在 Keycloak 中,轉到 **Clients** -> **ClientID** -> **Settings**,然後執行以下操作: 1. 在 “Client Protocol” 下拉菜單中,選擇 “openid-connect”。 2. 在 “Access Type” 下拉菜單中,選擇 “confidential”。 3. 在 “Valid Redirect URIs” 欄位中,輸入 LIFF 應用程序的 URL。例如,如果 LIFF 應用程序的 URL 為 https://example.com,則輸入 https://example.com/*。 4. 在 “Web Origins” 欄位中,輸入 LIFF 應用程序的 URL。例如,如果 LIFF 應用程序的 URL 為 https://example.com,則輸入 https://example.com。 5. 確定保存更改。 3. 配置 Keycloak 的用戶和角色。在 Keycloak 中,轉到 Users,然後創建新用戶。然後,在 Roles 下創建一些角色,例如 `user`。 4. 為 LIFF 應用程序生成一個專用的 access token。在 Keycloak 中,轉到 **Clients** -> **Client ID** -> **Credentials**,然後點擊 **Regenerate Secret** 生成新的 Client Secret。 5. 最後,將新生成的 Client Secret 設置為 Spring Boot 應用程序的參數,並配置 Spring Security 使用 Keycloak 進行身份驗證和授權,如上所述。 ## 這樣 Keycloak 就能從 LIFF 傳來的 access token 解析出用戶身分並保護 REST API 嗎 是的,這樣配置之後,當 LIFF 應用程序使用 access token 訪問 Spring Boot 的 REST API 時,Keycloak 可以使用 access token 中的信息進行用戶身份驗證,並根據用戶角色或權限保護 REST API 的訪問。 在 Spring Boot 應用程序中,你需要使用 Keycloak Spring Security Adapter 來實現此功能。Keycloak Spring Security Adapter 是一個 Keycloak 提供的用於 Spring Security 應用程序的庫,可用於簡化 Spring Security 的集成和配置。 在 Spring Boot 應用程序中,你可以將 Keycloak Spring Security Adapter 添加到依賴中,然後在 Spring Security 的配置中配置 Keycloak 的身份驗證和授權功能。這樣配置之後,當用戶訪問受保護的 REST API 時,Spring Security 將使用 Keycloak 中的用戶信息進行身份驗證和授權,並根據用戶角色或權限控制訪問權限。 ## 我不太理解 Keycloak 是怎麼解析出用戶身分的,不需要配置 JWKS 等等之類的嗎 當 LIFF 應用程序使用 access token 訪問 Spring Boot 的 REST API 時,Keycloak 將使用 access token 中的 JWT (JSON Web Token) 進行身份驗證。JWT 是一種開放標準,用於在不同應用程序之間傳遞安全信息。JWT 由三部分組成:Header、Payload 和 Signature。 在 JWT 中,Payload 包含有關用戶身份的信息,例如用戶名、電子郵件地址和角色。Keycloak 可以使用 JWT 的內容進行用戶身份驗證和授權。 在 LIFF 應用程序中,當使用者進行登入後,LIFF 會透過 Keycloak 發出 token 的 request,Keycloak 則會進行身份驗證,並在使用者身份驗證成功後返回一個包含 JWT 的 access token 給 LIFF 應用程序。 當 LIFF 應用程序使用 access token 訪問 Spring Boot 的 REST API 時,Keycloak 將從 access token 中解析出 JWT,然後使用 JWT 的內容進行用戶身份驗證和授權。因此,你不需要額外配置 JWKS 或其他類似的設置,只需要配置好 Keycloak 的 client 和角色即可。 ## 這種情況下,當有一個新的用戶產生時 Keycloak 怎麼知道呢 當有一個新的用戶產生時,你需要在 Keycloak 中手動創建用戶。你可以在 Keycloak 管理介面中的 Users 頁面上添加新用戶,或者使用 Keycloak 的 API 創建用戶。 在創建用戶時,你可以為用戶分配角色或權限,並將其關聯到相應的 client 上。在這種情況下,當用戶使用 LIFF 應用程序訪問 Spring Boot 的 REST API 時,Keycloak 將使用 JWT 的內容進行用戶身份驗證和授權,並根據用戶角色或權限控制訪問權限。 如果你希望自動創建 Keycloak 用戶,可以使用 Keycloak 的 API 進行自動化創建。你可以通過調用 Keycloak 的 REST API,使用相應的 API 請求在 Keycloak 中創建新用戶。但是,這需要你進行更多的開發工作,並需要保證安全性和可靠性。 ## 使用 API 自動創建 Keycloak 用戶在 Spring Boot 該怎麼做 要在 Spring Boot 中使用 Keycloak 的 API 創建用戶,你需要使用 Keycloak 的 Java 客戶端庫。以下是一個簡單的例子,展示了如何使用 Java 客戶端庫創建 Keycloak 用戶: 首先,你需要在你的 Spring Boot 項目中添加 Keycloak Java 客戶端庫的依賴項。你可以在 Maven 或 Gradle 配置文件中添加以下依賴項: ```xml <dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-admin-client</artifactId> <version>16.1.0</version> </dependency> ``` 或 ```xml implementation 'org.keycloak:keycloak-admin-client:16.1.0' ``` 然後,在你的 Spring Boot 項目中,你可以使用以下代碼創建一個 Keycloak 用戶: ```java // 創建 Keycloak 客戶端 Keycloak keycloak = KeycloakBuilder.builder() .serverUrl("http://localhost:8080/auth") .realm("your_realm") .clientId("your_client_id") .clientSecret("your_client_secret") .username("admin") .password("admin_password") .build(); // 創建用戶 UserRepresentation user = new UserRepresentation(); user.setUsername("user123"); user.setEmail("user123@example.com"); user.setEnabled(true); // 添加用戶到 Keycloak 中 Response response = keycloak.realm("your_realm").users().create(user); if (response.getStatus() != 201) { throw new RuntimeException("Failed to create user"); } ``` 這個代碼片段中,我們使用 KeycloakBuilder 創建了一個 Keycloak 客戶端,並使用客戶端創建了一個新用戶。在創建用戶時,我們還可以為用戶指定角色或其他屬性。最後,我們使用 Keycloak 的 REST API 將用戶添加到 Keycloak 中。 注意,你需要使用你自己的 Keycloak 設置替換上面代碼中的 "your_realm"、"your_client_id"、"your_client_secret"、"admin" 和 "admin_password" 等參數。 這樣,你就可以使用 Keycloak 的 Java 客戶端庫創建 Keycloak 用戶了。 ## 前述 Keycloak 參數 "your_realm"、"your_client_id"、"your_client_secret"、"admin" 和 "admin_password" 等能設置在 application.properties 嗎 是的,你可以將 Keycloak 的參數設置在 Spring Boot 的 `application.properties` 或 `application.yml` 文件中,並使用 Spring Boot 的屬性注入來注入這些參數。以下是一個使用 `application.properties` 文件設置 Keycloak 參數的例子: 在 `application.properties` 文件中添加以下配置: ```properties # Keycloak 配置 keycloak.auth-server-url=http://localhost:8080/auth keycloak.realm=your_realm keycloak.resource=your_client_id keycloak.credentials.secret=your_client_secret keycloak.admin.username=admin keycloak.admin.password=admin_password ``` 然後,在你的 Java 代碼中使用 @Value 注解來注入這些參數。例如,你可以創建一個名為 "keycloakConfig" 的類,並使用以下代碼來注入 Keycloak 參數: ```java @Component @ConfigurationProperties(prefix = "keycloak") public class KeycloakConfig { private String authServerUrl; private String realm; private String resource; private String credentialsSecret; private String adminUsername; private String adminPassword; // 省略 getter 和 setter 方法 } ``` 在這個例子中,我們使用 @Component 和 @ConfigurationProperties 注解將這個類聲明為 Spring Bean,並注入了 Keycloak 的各個參數。 然後,在你的代碼中,你可以使用 @Autowired 注解來注入 KeycloakConfig 類,並從這個類中獲取 Keycloak 的各個參數,例如: ```java @Autowired private KeycloakConfig keycloakConfig; public void createKeycloakUser() { // 創建 Keycloak 客戶端 Keycloak keycloak = KeycloakBuilder.builder() .serverUrl(keycloakConfig.getAuthServerUrl()) .realm(keycloakConfig.getRealm()) .clientId(keycloakConfig.getResource()) .clientSecret(keycloakConfig.getCredentialsSecret()) .username(keycloakConfig.getAdminUsername()) .password(keycloakConfig.getAdminPassword()) .build(); // 創建用戶 UserRepresentation user = new UserRepresentation(); user.setUsername("user123"); user.setEmail("user123@example.com"); user.setEnabled(true); // 添加用戶到 Keycloak 中 Response response = keycloak.realm(keycloakConfig.getRealm()).users().create(user); if (response.getStatus() != 201) { throw new RuntimeException("Failed to create user"); } } ``` 這樣,你就可以在 Spring Boot 中使用 application.properties 文件設置 Keycloak 參數並使用 Spring Boot 的屬性注入來注入這些參數了。 ## 前述使用 @Autowired 注解來注入 KeycloakConfig 類,是要在哪個類裡注入 你可以在需要使用 Keycloak 參數的 Spring Bean 中注入 KeycloakConfig 類,以便在這個 Bean 中使用這些參數。例如,如果你需要在一個 UserService 中創建 Keycloak 用戶,你可以在這個 UserService 中注入 KeycloakConfig 類,並使用這個類中的參數創建 Keycloak 客戶端。以下是一個示例: ```java @Service public class UserService { @Autowired private KeycloakConfig keycloakConfig; public void createUser(UserDto userDto) { // 創建 Keycloak 客戶端 Keycloak keycloak = KeycloakBuilder.builder() .serverUrl(keycloakConfig.getAuthServerUrl()) .realm(keycloakConfig.getRealm()) .clientId(keycloakConfig.getResource()) .clientSecret(keycloakConfig.getCredentialsSecret()) .username(keycloakConfig.getAdminUsername()) .password(keycloakConfig.getAdminPassword()) .build(); // 創建用戶 UserRepresentation user = new UserRepresentation(); user.setUsername(userDto.getUsername()); user.setEmail(userDto.getEmail()); user.setEnabled(true); // 添加用戶到 Keycloak 中 Response response = keycloak.realm(keycloakConfig.getRealm()).users().create(user); if (response.getStatus() != 201) { throw new RuntimeException("Failed to create user"); } } } ``` 在這個例子中,我們在 UserService 中使用 @Autowired 注解來注入 KeycloakConfig 類,並使用這個類中的參數創建 Keycloak 客戶端。這樣,當 UserService 的 createUser 方法被調用時,就可以使用這個 Keycloak 客戶端創建用戶了。 ## 若 `response.getStatus() != 201` 除了拋出 `RuntimeException` 還有沒有更好的實踐 當 Keycloak 創建用戶失敗時,拋出 RuntimeException 可以讓呼叫者知道出了什麼問題。不過,更好的實踐是使用自定義的異常類型,以便區分不同類型的錯誤。這樣做可以提高代碼的可讀性和可維護性,也更容易進行錯誤處理和調試。 你可以創建一個自定義的異常類型,例如 UserCreationFailedException,並在 createUser 方法中捕獲 Keycloak 創建用戶時可能拋出的異常,然後拋出 UserCreationFailedException 異常,以便在外層進行處理。以下是一個示例: ```java @Service public class UserService { // ... public void createUser(UserDto userDto) throws UserCreationFailedException { // ... // 添加用戶到 Keycloak 中 Response response = keycloak.realm(keycloakConfig.getRealm()).users().create(user); if (response.getStatus() != 201) { throw new UserCreationFailedException("Failed to create user"); } } } ``` 在這個例子中,我們定義了一個 UserCreationFailedException 異常類型,並在 createUser 方法中拋出這個異常,以便在外層進行處理。這樣,當創建用戶失敗時,就可以捕獲這個異常,並進行相應的處理。 ## 前述的 `KeycloakAuthenticationProvider` 中有個 `JwtDecoder` 的 `@Bean`,它的依存性是什麼 在 `KeycloakAuthenticationProvider` 中使用的 `JwtDecoder` `@Bean` 的依存性是 `org.springframework.security.oauth2.jwt.JwtDecoder`。這個依存性是 Spring Security OAuth2 中的一部分,用於解碼 JWT(JSON Web Tokens)。 在 Spring Security OAuth2 中,JwtDecoder 用於解碼 JWT,然後將其轉換為 OAuth2AuthenticationToken。JwtDecoder 的實現方式可以是對稱加密、非對稱加密、JWK Set 等,因此可以支持多種加密方式。 在 KeycloakAuthenticationProvider 中,JwtDecoder 用於解碼從 Keycloak 返回的 ID Token,並使用其內容創建 Authentication 對象。簡單來說,JwtDecoder 確保 ID Token 的有效性,並使用其內容創建一個 Authentication 對象,以便 Spring Security 進行授權。 為了使用 JwtDecoder,我們需要將 `org.springframework.boot:spring-boot-starter-oauth2-client` 依存性添加到 Maven 或 Gradle 項目中,或者將 spring-security-oauth2-client 依存性添加到舊版的 Spring Security 項目中。這樣,Spring Security 就能夠自動配置 JwtDecoder,以便使用 JWT 進行身份驗證和授權。
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.