J'essaie d'ajouter de la sécurité à mon application Spring Boot. Mon application actuelle utilise des contrôleurs REST et chaque fois que je reçois une demande GET
ou POST
, je lis l'en-tête HTTP pour récupérer l'utilisateur et le mot de passe afin de les valider par rapport au fichier de propriétés que j'ai stocké tous mes utilisateurs. Je veux changer cela en utilisant Spring Security et voici ce que j'ai obtenu jusqu'à présent:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/index.html").permitAll()
.antMatchers("/swagger-ui.html").hasRole("ADMIN")
.anyRequest().authenticated();
}
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("admin").password("password").roles("ADMIN").build());
}
}
Comment puis-je dire à la méthode configure
que les informations d'identification de l'utilisateur doivent être extraites de l'en-tête et non d'un formulaire de connexion?
Vous devez éviter d'utiliser par défaut org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
car il obtient le nom d'utilisateur et le mot de passe fournis par le client à partir des paramètres de votre demande et vous devez vraiment les obtenir à partir des en-têtes.
Donc, vous devez écrire un AuthenticationFilter
personnalisé étendu UsernamePasswordAuthenticationFilter
pour modifier son comportement en fonction de vos besoins:
public class HeaderUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
/**
*
*/
public HeaderUsernamePasswordAuthenticationFilter() {
super();
this.setFilterProcessesUrl("/**");
this.setPostOnly(false);
}
/* (non-Javadoc)
* @see org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#obtainPassword(javax.servlet.http.HttpServletRequest)
*/
@Override
protected String obtainPassword(HttpServletRequest request) {
return request.getHeader(this.getPasswordParameter());
}
/* (non-Javadoc)
* @see org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#obtainUsername(javax.servlet.http.HttpServletRequest)
*/
@Override
protected String obtainUsername(HttpServletRequest request) {
return request.getHeader(this.getPasswordParameter());
}
}
Cet exemple de filtre étend org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
écoute chaque demande et obtient username
et password
des en-têtes au lieu de parameters
.
Ensuite, vous devez modifier la configuration de cette façon, en définissant votre filtre sur la position UsernamePasswordAuthenticationFilter
:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAt(
new HeaderUsernamePasswordAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/index.html").permitAll()
.antMatchers("/swagger-ui.html").hasRole("ADMIN")
.anyRequest().authenticated();
}
L'authentification en mémoire servirait votre objectif
@Configuration
@EnableWebMvc
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user1").password("password1").roles("USER")
.and()
.withUser("user2").password("password2").roles("ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated();
http.httpBasic();
}
}
l'ajout minimal de code consiste à définir un filtre et à l'ajouter à la configuration de sécurité, comme smth
XHeaderAuthenticationFilter.Java
@Component
public class XHeaderAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String xAuth = request.getHeader("X-Authorization");
User user = findByToken(xAuth);
if (user == null) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token invalid");
} else {
final UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
//need to implement db user validation...
private User findByToken(String token) {
if (!token.equals("1234"))
return null;
final User user = new User(
"username",
"password",
true,
true,
true,
true,
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
return user;
}
}
SecurityConfig.Java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.authorizeRequests().anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
.and()
.addFilterBefore(new XHeaderAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
une autre approche consiste à utiliser AOP
de spring pour définir l'annotation d'une logique à exécuter avant d'entrer dans la méthode du contrôleur annoté