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
En el método
@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();
}
}
configure()
especificamos que todos los request a /personas* tienen que tener el rol privilegiado.Y la clase para lanzar Spring Boot:
Para no extender el ejemplo quedan los metodos de recursos en la misma clase:
@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);
}
}
- 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: