Configurar Keycloak
Configurar Spring Boot
Configurar React
Keycloak
Keycloak se puede obtener utilizando docker o desde su página como Standalone server distribution (previamente en este enlace había indicado como obtenerlo y lanzarlo).
Luego de lanzarlo estará disponible localmente y procedemos a realizar su configuración (en mi caso visitando http://localhost:8282/):
Si es la primera vez configuramos el usuario administrador. Una vez ingresado entramos a la aplicación:
Seleccionamos Add realm y definimos su nombre, en este ejemplo es: sso
Luego vamos a Clients y presionamos Create para agregar dos clientes: cliente-uno para Spring Boot y cliente-dos para React, en ambos definimos su Root URL y Web Origins (NO olvidar presionar el boton save cuando realizamos modificaciones, web origins puede ser más estricto, es * para el ejemplo):
Ahora creamos un Role para el Realm sso (en este ejemplo es privilegiado):
Luego creamos un usuario (sebastian) y lo asociamos al rol que creamos:
Spring Boot
@Configuration
@EnableWebSecurity
public class KeycloakSecurity extends KeycloakWebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
final var kap = keycloakAuthenticationProvider();
kap.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(kap);
}
@Bean
public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
super.configure(http);
http.cors().and().csrf().disable().authorizeRequests().antMatchers("/personas*")
.hasRole("privilegiado").anyRequest().permitAll();
}
}
En el método configure()
especificamos que todos los request a /personas* tienen que tener el rol privilegiado.Y la clase para lanzar Spring Boot:
@SpringBootApplication
@RequestMapping
@CrossOrigin(allowCredentials="true")
public class SbKeycloakApplication {
@GetMapping(path = "/")
public ResponseEntity saludo() {
return ResponseEntity.ok(new Persona(3, "tres"));
}
@GetMapping(path = "/personas")
public ResponseEntity> customers(final Principal principal, final Model model) {
final var personas = new ArrayList();
personas.add(new Persona(1, "uno"));
personas.add(new Persona(2, "dos"));
return ResponseEntity.ok(personas);
}
public static void main(final String[] args) {
SpringApplication.run(SbKeycloakApplication.class, args);
}
}
Para no extender el ejemplo quedan los metodos de recursos en la misma clase:- request a / no requieren autorizacion
- request a /personas requieren autorizacion
keycloak.principal-attribute=preferred_username
keycloak.auth-server-url=http://localhost:8282/auth
keycloak.realm=sso
keycloak.resource=cliente-uno
keycloak.public-client=true
Si lanzamos la aplicación y accedemos al http://localhost:8080 obtenemos lo siguiente:
es de acceso libre. Ahora accedemos al http://localhost:8080/personas y no podemos acceder inmediatamente:
React
- propagará las credenciales al backend (spring boot)
- permitirá acceder al inicio (público) y a los recursos protegidos (keycloak)
- dentro de la carpeta public tiene que quedar un archivo keycloak.json con el siguiente contenido (que puede variar dependiendo de la instalación):
{ "realm": "sso", "auth-server-url": "http://localhost:8282/auth", "ssl-required": "external", "resource": "cliente-dos", "public-client": true, "confidential-port": 0, "enable-cors": true }
- Ahora creamos los metodos relacionados con obtener los datos desde el backend en el componente Protegido.js:
componentDidMount() { const keycloak = Keycloak('/keycloak.json'); keycloak.init({onLoad: 'login-required'}).then(authenticated => { this.setState({ keycloak: keycloak, authenticated: authenticated }); this.cargarDatosProtegidos(); }) } authorizationHeader() { if(!this.state.keycloak) return {}; return { headers: { "Authorization": "Bearer " + this.state.keycloak.token } }; } cargarDatosProtegidos() { fetch('http://localhost:8080/personas', this.authorizationHeader()) .then(response => response.json()) .then(result => this.setState({datos: result})) .catch(error => console.log(error)); } render() { if (this.state.keycloak) { if (this.state.authenticated) return ...codigo-contenido-autorizado...; else return ...codigo-error... } return ...codigo-no-autorizado...; } }
- cuando se carga el componente carga los datos de acceso a keycloak e intenta cargar los datos protegidos del backend, si no tiene las credenciales se mostrará la pagina de autenticación del keycloak, luego se agrega al header del request y se accede a los recursos protegidos:
- También está el componente Logout.js que realiza el logout en keycloak:
logout() { this.props.history.push('/'); this.props.keycloak.logout(); }
Y eso es todo 😌, está keycloak configurado, recursos de spring boot protegidos y la vista con React hermosa 😖. Ahora podemos continuar creando microservicios seguros 😁.
Enlaces del código fuente: