3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SpringBoot Java で 簡単なLINE認証を実装してECSにデプロイする

Last updated at Posted at 2025-01-26

目的

LINE SNS k認証機能を実装し、LINE認証の仕組みと機能実装方法を理解すること

構築環境と必要なもの

  • Mac(windowsでも可能)
    • Terraformコマンドを叩けること
  • LINEアカウント
  • AWSアカウント

どう作るか

構成の説明

AWS環境で構築・公開を行う
ECSへデプロイする
LINE認証Callbackドメインの登録は ALB の DNS(Aレコード)を登録する

内部ドメイン

  • /
    • トップページを表示
  • /login
    • login認証ドメインへリダイレクトする
  • /callback
    • LINE認証後にリクエストされるドメイン
    • / へリダイレクトする

大まかな手順

  1. Webアプリケーションを開発
  2. アプリケーションのImageを作成
  3. ImageをECRへPush
  4. インフラ環境を構築 & アプリケーションをデプロイ
  5. LINE CHANNELを作成 & CallBackURLを登録
  6. 動作確認

開発

  1. Webアプリケーションを開発
    以下のRepositoryをcloneすると早い
    https://github.com/GitEngHar/SnsAuthenticationApp/pull/1

    • 認証用 フロントエンド
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Hello</title>
    </head>
    <body>
        <p>HelloWorld</p>
        <!--    リダイレクト機能確認用URL-->
        <a th:href="@{/redirect}">リダイレクト機能テスト</a><br>
        <a th:href="@{/login}">ログイン</a>
    </body>
    </html>
    
    • トップページを表示するController
    package com.example.snsauthenticationapp.web;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /** Topページ*/
    
    @Controller
    @RequestMapping("/")
    public class IndexController {
    
        /** トップページ */
        @GetMapping("")
        public String index(Model model) {
            return "index";
        }
    
        /** リダイレクト確認用実装 */
        @GetMapping("redirect")
        public String redirect() {
            return "redirect:/";
        }
    
    }
    
    
    • 認証リダイレクト処理 と CallBack処理
    package com.example.snsauthenticationapp.web;
    
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.servlet.view.RedirectView;
    
    /**
     * LINE認証リダイレクトURLへリダイレクト
     * CallBackハンドリング
     * */
    
    @Controller
    public class LoginController {
    
        final LineAuthenticationService lineAuthenticationService;
    
        public LoginController(LineAuthenticationService lineAuthenticationService){
            this.lineAuthenticationService = lineAuthenticationService;
        }
    
        /** LINE認証用リダイレクト */
        @GetMapping("/login")
        public RedirectView login() {
            String redirectUrl = lineAuthenticationService.createRedirectURL();
            return new RedirectView(redirectUrl);
        }
    		
    		/** CallBack処理 */
        @RequestMapping(value = "/callback", method = RequestMethod.GET)
        public String getToken(@RequestParam("code") String code,
                               HttpServletRequest req, HttpServletResponse res) throws Exception{
            return "redirect:/";
        }
    }
    
    
    • LINE認証用 プロパティ をオブジェクトへ代入する
    package com.example.snsauthenticationapp.domain;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    /**
     * 認可コードの取得
     * アクセストークンの発行 / ユーザー情報を取得
     * */
    @Component
    @ConfigurationProperties(prefix = "lineauth")
    public class LineAuthElement {
        private String responseType;
        private String redirectUri;
        private String clientId;
        private String scope;
        private String state;
    
        public String getState() {
            return state;
        }
    
        public void setState(String state) {
            this.state = state;
        }
    
        public String getResponseType() {
            return responseType;
        }
    
        public void setResponseType(String responseType) {
            this.responseType = responseType;
        }
    
        public String getRedirectUri() {
            return redirectUri;
        }
    
        public void setRedirectUri(String redirectUri) {
            this.redirectUri = redirectUri;
        }
    
        public String getClientId() {
            return clientId;
        }
    
        public void setClientId(String clientId) {
            this.clientId = clientId;
        }
    
        public String getScope() {
            return scope;
        }
    
        public void setScope(String scope) {
            this.scope = scope;
        }
    }
    
    
    • 認証ドメインの生成
    package com.example.snsauthenticationapp.web;
    
    import java.util.ArrayList;
    import java.util.List;
    import org.springframework.stereotype.Service;
    import org.apache.http.NameValuePair;
    import org.apache.http.message.BasicNameValuePair;
    import org.apache.http.client.utils.URIBuilder;
    import org.apache.commons.lang3.RandomStringUtils;
    import com.example.snsauthenticationapp.domain.LineAuthElement;
    
    /**
     * state, nonce用 検証ランダム文字列の生成
     * アクセストークンの検証や破棄
     * */
    @Service
    public class LineAuthenticationService {
        private final LineAuthElement lineAuthElement;
        public LineAuthenticationService(LineAuthElement lineAuthElement) {
            this.lineAuthElement = lineAuthElement;
        }
    
        /** リダイレクト先のドメインを生成する
         * https://access.line.me/oauth2/v2.1/authorize?response_type=code&client_id=1234567890&redirect_uri=https%3A%2F%2Fexample.com%2Fauth%3Fkey%3Dvalue&state=12345abcde&scope=profile%20openid&nonce=09876xyz
         * */
        public String createRedirectURL() {
            try {
                // csrf検証用 stringを生成
                lineAuthElement.setState(createUniqueState());
    
                // 認証用リダイレクトパラメータを生成
                List<NameValuePair> params = new ArrayList<>();
                params.add(new BasicNameValuePair("response_type", lineAuthElement.getResponseType()));
                params.add(new BasicNameValuePair("client_id", lineAuthElement.getClientId()));
                params.add(new BasicNameValuePair("redirect_uri", lineAuthElement.getRedirectUri()));
                params.add(new BasicNameValuePair("scope", lineAuthElement.getScope()));
                params.add(new BasicNameValuePair("state", lineAuthElement.getState()));
    
                // 認証用ドメインを生成
                URIBuilder builder = new URIBuilder();
                builder.setScheme("https");
                builder.setHost("access.line.me");
                builder.setPath("oauth2/v2.1/authorize");
    
                // 認証用ドメインにパラメータを付与
                builder.setParameters(params);
    
                // リダイレクトURIを生成
                return builder.build().toURL().toString();
    
            } catch (Exception e) {
                System.out.println(e.getMessage());
                return null;
            }
        }
    
        private static String createUniqueState(){
            return RandomStringUtils.randomAlphanumeric(20);
        }
    }
    
    
    • 認証用property
    spring:
      application:
        name: "SnsAuthenticationApp"
    lineauth:
      response_type: "code"
      client_id: ${CLIENT_ID}
      redirect_uri: ${REDIRECT_URI}
      # state
      scope: "profile"
      # nonce
      #  prompt
      #  max_age
      #  ui_locales
      #  bot_prompt
      #  initial_amr_display
      #  switch_amr
      #  disable_auto_login
      #  disable_ios_auto_login
      #  code_challenge
      #  code_challenge_method
    
    
  2. アプリケーションのImageを作成
    JarのBuild IntelliJ

    ~/Documents/SnsAuthenticationApp
    $ ./gradlew build
    

    Jarの出力先

    作業場所/リポジトリ名/build/libs

    
    ~/Documents/SnsAuthenticationApp/build/libs
    

    JarでWebAppが動作することを確認

    # jarを起動
    java -jar SnsAuthenticationApp-0.0.1-SNAPSHOT.jar
    
    # 起動ログ
      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
    
     :: Spring Boot ::                (v3.4.1)
    
    2025-01-19T10:22:06.301+09:00  INFO 41855 --- [SnsAuthenticationApp] [           main] c.e.s.SnsAuthenticationAppApplication    : Starting SnsAuthenticationAppApplication v0.0.1-SNAPSHOT using Java 23.0.1 with PID 41855 (/Users/harukisugiyama/Documents/SnsAuthenticationApp/build/libs/SnsAuthenticationApp-0.0.1-SNAPSHOT.jar started by harukisugiyama in /Users/harukisugiyama/Documents/SnsAuthenticationApp/build/libs)
    2025-01-19T10:22:06.302+09:00  INFO 41855 --- [SnsAuthenticationApp] [           main] c.e.s.SnsAuthenticationAppApplication    : No active profile set, falling back to 1 default profile: "default"
    2025-01-19T10:22:06.572+09:00  INFO 41855 --- [SnsAuthenticationApp] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
    2025-01-19T10:22:06.577+09:00  INFO 41855 --- [SnsAuthenticationApp] [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
    2025-01-19T10:22:06.577+09:00  INFO 41855 --- [SnsAuthenticationApp] [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.34]
    2025-01-19T10:22:06.588+09:00  INFO 41855 --- [SnsAuthenticationApp] [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
    2025-01-19T10:22:06.589+09:00  INFO 41855 --- [SnsAuthenticationApp] [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 270 ms
    2025-01-19T10:22:06.624+09:00  INFO 41855 --- [SnsAuthenticationApp] [           main] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page template: index
    2025-01-19T10:22:06.708+09:00  INFO 41855 --- [SnsAuthenticationApp] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
    2025-01-19T10:22:06.715+09:00  INFO 41855 --- [SnsAuthenticationApp] [           main] c.e.s.SnsAuthenticationAppApplication    : Started SnsAuthenticationAppApplication in 0.538 seconds (process running for 0.702)
    2025-01-19T10:22:15.885+09:00  INFO 41855 --- [SnsAuthenticationApp] [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
    2025-01-19T10:22:15.885+09:00  INFO 41855 --- [SnsAuthenticationApp] [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
    2025-01-19T10:22:15.886+09:00  INFO 41855 --- [SnsAuthenticationApp] [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
    
    

    動作確認

    # localにcurlする
    ~/Documents/SnsAuthenticationApp/build/libs
    ❯ curl localhost:8080
    
    # 出力
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Hello</title>
    </head>
    <body>
        <p>HelloWorld</p>
    </body>
    </html>
    

    DockerFileの作成

    # Java build Base Image
    FROM eclipse-temurin
    
    # 作業ディレクトリを設定
    WORKDIR /app
    
    # Buildするjarをコピー
    COPY ./app/SnsAuthenticationApp-0.0.1-SNAPSHOT.jar app.jar
    
    # 環境変数を設定(検証用)
    # ENV REDIRECT_URI=hogepoge
    
    # portを設定
    EXPOSE 8080
    
    # jarの起動
    ENTRYPOINT ["java", "-jar", "app.jar"]
    

    Docker Build

    docker build -t sbs-authentication-app . --platform linux/amd64
    
  3. ImageをECRへPush
    「プッシュコマンドを表示」をクリックして内容を参考に認証してpush
    image.png

  4. インフラ環境を構築 & アプリケーションをデプロイ

    1. Repositoryをcloneする
      https://github.com/GitEngHar/TfSnsAuthenticationApp
    2. Repository内に terraform.tfvars を作成
      • aws_account_id = "1234" のように環境変数を設定する
    3. terraform apply でリソースを構築
  5. LINE CHANNELを作成 & CallBackURLを登録

    • ここを参考にCHANNELを作成する
    • AWS ALB の DNSを取得し CallBackURLを設定する 参考
  6. 動作確認 (ALB DNS(Aレコード) を取得して設定)

  • 自分のLINEアカウントが表示されて認証後 リダイレクト先にアクセスできたら成功

苦労したこと

検証する際にBuildImageのアーキテクチャと実行アーキテクチャの差でエラー

  • ビルドしたImageでコンテナを立てるためにMacに適した platformを指定してimageをbuildする必要があった
    • amd64 でアーキテクチャを指定して Imageを構築する

実際のエラー


 JRE version:  (21.0.5+11) (build )
2025-01-19T01:27:39.572762094Z # Java VM: OpenJDK 64-Bit Server VM (21.0.5+11-LTS, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, linux-aarch64)
2025-01-19T01:27:39.572762844Z # Problematic frame:
2025-01-19T01:27:39.572763469Z # j  java.lang.System.registerNatives()V+0 java.base@21.0.5
2025-01-19T01:27:39.572764094Z #
2025-01-19T01:27:39.572764594Z # No core dump will be written. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
2025-01-19T01:27:39.572765427Z #
2025-01-19T01:27:39.573900719Z # An error report file with more information is saved as:
2025-01-19T01:27:39.573961052Z # /app/hs_err_pid1.log
2025-01-19T01:27:39.577807927Z [0.024s][warning][os] Loading hsdis library failed
2025-01-19T01:27:39.580877844Z #
2025-01-19T01:27:39.580902261Z # The crash happened outside the Java Virtual Machine in native code.
2025-01-19T01:27:39.580904302Z # See problematic frame for where to report the bug.
2025-01-19T01:27:39.580905594Z #
2025-01-19T01:27:39.580906636Z 
2025-01-19T01:27:39.580907886Z [error occurred during error reporting (), id 0x5, SIGTRAP (0x5) at pc=0x0000ffffadf57ebc]

解決

ビルド時に アーキテクチャ (--platform linux/amd64) を指定

docker build -t spring-docker . --platform linux/amd64

参考

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?