I am looking for a way to secure methods with the @Secured
annotation of Spring Boot. For about 10-15 users, I wouldn't want to connect to a database and obtain the users and their authorities/roles from there but rather store them locally in the profile-specific application.yml
file. Is there a concept in Spring Boot supporting this idea? All I could find so far works with the basic security actuator ('org.springframework.boot:spring-boot-starter-security'
) and looks like this:
security:
basic:
enabled: true
user:
name: admin
password: admin
role: EXAMPLE
However, I'm still able to access a method annotated with @RolesAllowed("READ")
even though I would assume that user admin shouldn't have access to said method. My SecurityConfiguration looks like this:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled = true)
@Profile("secure")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.fullyAuthenticated()
.and()
.httpBasic();
http.sessionManagement()
.sessionFixation()
.newSession();
http.csrf().disable();
http.headers().frameOptions().disable();
}
}
Eventually this might be a different issue but maybe it's important for my own understanding.
I'm wondering how I could specify multiple users with different passwords and different roles in my application.yml
and annotate methods to ensure only authorized users can access the methods.
Configure Default Username, Password and Role To configure the default username, password and role, open application. properties file of your Spring Boot project and add the following three properties with the values you prefer. The above properties will change the default username, password and role.
In the basic authentication, we send a username and password as part of our request. When we provide a username and password, it allows us to access the resource. There are other advanced forms of authentication like digest authentication, where the password digest is created, and the digest is sent across.
User, Role and PrivilegeThe Role represents the high-level roles of the user in the system. Each role will have a set of low-level privileges. The Privilege represents a low-level, granular privilege/authority in the system.
It can be achieved with custom ConfigurationProperties
:
@ConfigurationProperties("application")
public class ApplicationClients {
private final List<ApplicationClient> clients = new ArrayList<>();
public List<ApplicationClient> getClients() {
return this.clients;
}
}
@Getter
@Setter
public class ApplicationClient {
private String username;
private String password;
private String[] roles;
}
@Configuration
@EnableConfigurationProperties(ApplicationClients.class)
public class AuthenticationManagerConfig extends
GlobalAuthenticationConfigurerAdapter {
@Autowired
ApplicationClients application;
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
for (ApplicationClient client : application.getClients()) {
auth.inMemoryAuthentication()
.withUser(client.getUsername()).password(client.getPassword()).roles(client.getRoles());
}
}
}
and then you can specify the users in your application.yml
:
application:
clients:
- username: rw
password: rw
roles: READ,WRITE
- username: r
password: r
roles: READ
- username: w
password: w
roles: WRITE
don't forget to add spring-boot-configuration-processor
to your build.gradle
:
compile 'org.springframework.boot:spring-boot-configuration-processor'
For Spring Boot 2.0, I use the following class:
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
@EnableWebSecurity
@ConditionalOnWebApplication
@EnableConfigurationProperties(ApplicationClients.class)
@RequiredArgsConstructor
@Slf4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final ApplicationClients application;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.requestMatchers(EndpointRequest.to("health")).permitAll()
.requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ACTUATOR")
.antMatchers("/rest/**").authenticated()
.antMatchers("/soap/**").authenticated()
.and()
.cors()
.and()
.httpBasic();
}
@Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
final InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
log.info("Importing {} clients:", application.getClients().size());
application.getClients().forEach(client -> {
manager.createUser(User.withDefaultPasswordEncoder()
.username(client.getUsername())
.password(client.getPassword())
.roles(client.getRoles())
.build());
log.info("Imported client {}", client.toString());
});
return manager;
}
}
Please keep in mind that User.withDefaultPasswordEncoder()
is marked deprecated because of security-concerns.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With