LoginSignup
1
1

More than 3 years have passed since last update.

Keycloakでマルチプロジェクト

Posted at

はじめに

複数プロジェクトでマイクロサービスを利用するために、リクエストごとにKeycloakのレルムの書き換えを行ってみたが、レルムごとにユーザー管理するのは現実的ではないことに気付き、Keycloakで行っていた認証・認可の認可の部分を切り離すことにした。

試したこと

バックエンドのマイクロサービスをSpring Bootで実装していたので、SpringのSecurity Filter Chainに、新しい認可情報を付加するフィルターを追加してみた。

プロジェクトごとに電子メールアドレスをユーザーの一意識別子としてメンバーを登録すれば、KeycloakのJWTトークンの電子メールアドレスでユーザーの名寄せができるという理屈です。

同じユーザーが複数のプロジェクトに参加するユースケースでは、プロジェクトごとに認可情報を管理したいのでKeycloakから切り離した方が自由度が高くなります。

尚、新しいフィルターの追加場所は、SessionManagementFilter.classの後が良さそうです。

ソースコード

public class CustomKeycloakFilter implements Filter {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final ProjectMemberService service;

    @Override
    public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
        logger.debug("######## CUSTOM KEYCLOAK FILTER INITIALIZED ########");
    }

    public CustomKeycloakFilter(ProjectMemberService service) {
        this.service = service;
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 
            throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        logger.debug("######## CUSTOM KEYCLOAK FILTER ########");

        // extract project id from the request header
        String projectId = request.getHeader("project-id");
        logger.debug("++++++++ PROJECT ID: {} ++++++++", projectId);

        // extract Kycloak authentication token
        Principal principal = request.getUserPrincipal();
        if (principal != null && principal instanceof KeycloakAuthenticationToken) {

            logger.debug("++++++++ AUTHENTICATED BY KEYCLOAK ++++++++", principal.getClass().getName());

            KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) principal;
            KeycloakSecurityContext context = (KeycloakSecurityContext)token.getAccount().getKeycloakSecurityContext();
            AccessToken accessToken = context.getToken();

            // extract email from the access token as a unique user identifier.
            String email = accessToken.getEmail();

            logger.debug("++++++++ email: {} ++++++++", email);

            if (projectId != null && email != null) {

                // retrieve project user permissions from the project database by project id and user email 
                Collection<? extends GrantedAuthority> authorities = service.getAuthz(UUID.fromString(projectId), email);

                List<String> authorityStringList = new ArrayList<>();
                authorities.forEach(o -> authorityStringList.add(o.getAuthority()));
                logger.debug("++++++++ AUTHOTITIES: {} ++++++++", authorityStringList);

                // generates a new authentication token with reloaded user permissions
                KeycloakAuthenticationToken newAuthenticationToken = new KeycloakAuthenticationToken(token.getAccount(), false, authorities);

                logger.debug("++++++++ NEW AUTHENTICATION TOKEN: {} ++++++++", newAuthenticationToken.toString());

                SecurityContext sc = SecurityContextHolder.getContext();

                // replaces authentication token with the new one
                sc.setAuthentication(newAuthenticationToken);
            }
        }
        chain.doFilter(request, response);
    }
}

まとめ

Keycloakの認証・認可の認可の部分を切り離すことにより、マルチプロジェクト(マルチテナント)に対応することができた。認可情報をプロジェクトごとに管理できるので、自由度の高いプロジェクト運用ができそうです。

参考資料

1
1
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
1
1