Afin d'utiliser des micro-services dans plusieurs projets, j'ai essayé de réécrire le domaine de Keycloak pour chaque demande, mais je me suis rendu compte qu'il n'était pas réaliste de gérer les utilisateurs pour chaque domaine, et l'authentification / autorisation qui a été faite avec Keycloak J'ai décidé de séparer la partie autorisation.
Depuis que le microservice backend a été implémenté dans Spring Boot, j'ai ajouté un filtre pour ajouter de nouvelles informations d'autorisation à la chaîne de filtres de sécurité de Spring.
La théorie est que si vous enregistrez un membre avec l'adresse e-mail comme identifiant unique de l'utilisateur pour chaque projet, vous pouvez identifier l'utilisateur avec l'adresse e-mail du jeton JWT de Keycloak.
Dans les cas d'utilisation où le même utilisateur participe à plusieurs projets, il est plus flexible de le séparer de Keycloak car vous souhaitez gérer les informations d'autorisation pour chaque projet.
De plus, la place pour ajouter un nouveau filtre semble être bonne après 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);
}
}
En séparant la partie autorisation de l'authentification / autorisation de Keycloak, il était possible de prendre en charge le multi-projet (~~ multi-tenant ~~). Étant donné que les informations d'autorisation peuvent être gérées pour chaque projet, il semble que le projet puisse être exploité avec un degré élevé de liberté.