I use overridden values for spring security username and password. Following properties are in my
application.properties
spring.security.user.name=myUser
spring.security.user.password=myPassword
spring.security.user.roles=admin
I would like to encrypt the password value as follows:
spring.security.user.name=myUser
spring.security.user.password={bcrypt}hashedpassword valuevalue
spring.security.user.roles=admin
I added PasswordEncoder in my SpringConfig:
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
In some example I noticed that there is for AuthenitcationManagerBuilder but I do not know what datasource should be used. What else do I need to use encrypted password for default user?
@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth)
throws Exception {
Adding my spring security config as a reference:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests().antMatchers("/api/v1/custom").hasRole("admin")
.anyRequest()
.authenticated()
.and()
.httpBasic();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
CodePudding user response:
Grabbing:
$2a$12$7Bg57uTtN7GWIdiqRW4h5e/aOUFagHwkEGv9byUr0bb/QbUU8S4rS
for input:
hello
... from (e.g. thx): https://bcrypt-generator.com/
With:
- spring-starter (web, security),
application.properties
:spring.security.user.password={bcrypt}$2a$12$7Bg57uTtN7GWIdiqRW4h5e/aOUFagHwkEGv9byUr0bb/QbUU8S4rS
- and any (rest)controller:
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @RestController public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } // @Bean // public PasswordEncoder encoder() { // return new BCryptPasswordEncoder(); // } @GetMapping("secured") public String secured() { return "Hello Admin"; } }
- and without additional config,
- we can login with
user=user
andpassword=hello
.
Adding:
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
breaks this behavior!! To "fix it", we can (application.properties
):
spring.security.user.password=$2a$12$7Bg57uTtN7GWIdiqRW4h5e/aOUFagHwkEGv9byUr0bb/QbUU8S4rS
.. omit the "tag" {bcrypt}
! ...
Responsible for "tags"/capable of several algorithms is the DelegatingPasswordEncoder
, which is auto configured (via [AuthenticationConfiguration|HttpSecurityConfiguration].LazyPasswordEncoder
..if no other PasswordEncoder bean found) and uses
PasswordEncoderFactories
behind the scenes, like (currently) this:
@SuppressWarnings("deprecation") public static PasswordEncoder createDelegatingPasswordEncoder() { String encodingId = "bcrypt"; Map<String, PasswordEncoder> encoders = new HashMap<>(); encoders.put(encodingId, new BCryptPasswordEncoder()); encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder()); encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder()); encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5")); encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance()); encoders.put("pbkdf2", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5()); encoders.put("pbkdf2@SpringSecurity_v5_8", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8()); encoders.put("scrypt", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1()); encoders.put("scrypt@SpringSecurity_v5_8", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8()); encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1")); encoders.put("SHA-256", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256")); encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder()); encoders.put("argon2", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2()); encoders.put("argon2@SpringSecurity_v5_8", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8()); return new DelegatingPasswordEncoder(encodingId, encoders); }
EDIT:
No, also introducing:
@Configuration
class WebSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeHttpRequests().requestMatchers("/secured").hasRole("admin")
.anyRequest()
.authenticated()
.and()
.httpBasic();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
}
}
Plus:
spring.security.user.roles=admin
Doesn't change my observations:
- When "custom password encoder"
- and
$2a$12$7Bg57uTtN7GWIdiqRW4h5e/aOUFagHwkEGv9byUr0bb/QbUU8S4rS
- can login with
user:hello
(and view/secured
response)
- can login with
- with default/-out password encoder
- and
{bcrypt}$2a$12$7Bg57uTtN7GWIdiqRW4h5e/aOUFagHwkEGv9byUr0bb/QbUU8S4rS
- can login with
user:hello
(and view/secured
response)
- can login with
(it only switches from form to basic login:)
Test (good/app config):
package com.example.demo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest
class WithAppConfigTest {
@Autowired
MockMvc mockMvc;
@Test
void expectAccess() throws Exception {
mockMvc.perform(
get("/secured")
.with(httpBasic("user", "hello"))
).andExpectAll(
authenticated(),
status().isOk(),
content().string("Hello Admin"));
}
@Test
void expectUnauthorized() throws Exception {
mockMvc.perform(
get("/secured")
.with(httpBasic("user", "wrong"))
).andExpectAll(
unauthenticated(),
status().isUnauthorized(),
content().bytes(new byte[0]));
}
}
Test forbidden:
@WebMvcTest(properties = "spring.security.user.roles=foo,bar,baz")
class WithFakeConfigTest {
@Autowired
MockMvc mockMvc;
@Test
void expectForbidden() throws Exception {
mockMvc.perform(
get("/secured")
.with(httpBasic("user", "hello"))
).andExpectAll(
authenticated(), // !, but:
status().isForbidden(),
content().bytes(new byte[0]));
}
}