I have a list of users, and I want to use them in my basic auth.
MY code currently looks like this:
#Configuration
#EnableWebSecurity
public class BasicAuthConfig extends WebSecurityConfigurerAdapter {
#Bean
public PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}
#Autowired
private ConfigService configService;
// Authentication : User --> Roles
// NoOpPasswordEncoder has been deprecated in Spring security so {noop} is being used to avoid errors
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication().passwordEncoder(passwordEncoder())
.withUser("someuser")
.password("somepassword")
.roles("USER");
}
// Authorization : Role -> Access
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and().authorizeRequests()
.antMatchers("/actuator/**")
.permitAll()
.antMatchers("/tokenservice/**")
.hasRole("USER")
.antMatchers("/")
.permitAll()
.and().csrf()
.disable()
.headers()
.frameOptions()
.and().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}}
I want to replace "someuser" and "somepassword" with usernames and passwords from my list of users. Currently I can get the list with configService.getCOnfigurations().getUsers().
A user just has a username and a password, both strings. How do I go about getting all the usernames and all the passwords into .withUser()?
**EDIT
I made a simple for loop in the configure, that should do it, but whenever i try to post to my API, it says org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder:99 - Encoded password does not look like BCrypt
I used an online bcrypt generator to generate the passwords, and they look like this
<?xml version="1.0" encoding="UTF-8"?>
<Configurations>
<Port>8007</Port>
<EnableHttps>true</EnableHttps>
<KeyStorePath>classpath:ssl-server.jks</KeyStorePath>
<KeyPass>changeit</KeyPass>
<TokenTtlMillis>15000</TokenTtlMillis>
<Users Username="user1">
<Password>$2y$10$.8VQR6tJub5uVdVLByItQO8QYGZVuWPhLuBUTQSDJAvVpLAUmuqZ2</Password>
</Users>
<Users Username="user2">
<Password>$2y$10$r/CQz7PZp5banmSzr9OiDe2Kxrda4BhXIBXvvouRnm1w3M72wLQj.</Password>
</Users>
</Configurations>
the passwords are in plain just password and password2
Building on Claudio's answer with the DaoAuthenticationProvider:
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService());
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailsService())
.authenticationProvider(authenticationProvider());
}
#Override
protected UserDetailsService userDetailsService() {
return new MyUserDetailsService();
}
The UserDetailsService is where the real meat of your code would be. You would provide a custom implementation of the interface that reads from your XML. Assuming that you have a method getPassword(String username):
// Adding this import to demontrate where "User" is coming from
import org.springframework.security.core.userdetails.User;
public class MyUserDetailsService implements UserDetailsService {
#Override
public User loadUserByUsername(String username) {
return new User(username, getPassword(username), Arrays.asList(new SimpleGrantedAuthority("USER")));
}
private String getPassword(String username) {
// Get password from your XML
}
}
As for your BCrypt issue, the password hash gives me an invalid salt revision error. Try using your app directly to hash it, e.g.:
public static void main(String[] args) {
System.out.println(new BCryptPasswordEncoder().encode("password"));
}
Or to pass in a file with a password on each line (using Java 8):
public static void main(String[] args) throws IOException {
if (args.length != 1) {
System.out.println("Requires 1 parameter that points to a file.");
System.exit(1);
}
File f = new File(args[0]);
if (!f.isFile()) {
System.out.println("Not a file: " + f);
System.exit(1);
}
PasswordEncoder encoder = new BCryptPasswordEncoder();
try (Stream<String> lines = Files.lines(f.toPath())) {
lines.map(encoder::encode)
.forEach(System.out::println);
}
}
That will give you the Spring-generated hashes which you can then insert into your XML.
You can declare a DaoAuthentificationProvider in your WebSecurityConfigurerAdapter like this:
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService());
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
and give it your implementation of a passwordEncoder and a userDetailsService,
for which you have to implement the respective interfaces and their methods.
And then you can assign your authenticationProvider in your WebSecurityConfigurerAdapter class like this:
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
auth.authenticationProvider(authenticationProvider());
}
This way, your UserDetailService will provide all the available users and their credentials and you don't have to worry about that in the security configuration.
This way you can store the credentials in whatever way you want (simple file, nosql DB like MongoDB, etc.) and even change that implementation without impact on the way you authenticate with spring security.
Your UserDetailService should look somewhat like this:
public class SecUserDetailsService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository().findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username);
} else {
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
grantedAuthorities.add(new SimpleGrantedAuthority(user.getRole().getName()));
return new org.springframework.security.core.userdetails.User(user.getName(), user.getPassword(),
grantedAuthorities);
}
}
}
Here I've used a UserRepository that takes care of loading all users from the storage of your choice. E.g. if you decide to store it in a file, it will load all users and their passwords from a file, and provide the method findByUsername that gives back the User object if one with a matching name is found. Your Repository can also take care of deleting users or modifying their names if needed.
I implemented this on spring boot 2.x, with getting users credentials from the console on server start; you can easily change it to load users from file or any other source:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger log = LogManager.getLogger();
#Override
protected void configure(HttpSecurity http) throws Exception {
// Note:
// Use this to enable the tomcat basic authentication (tomcat popup rather than spring login page)
// Note that the CSRf token is disabled for all requests
log.info("Disabling CSRF, enabling basic authentication...");
http
.authorizeRequests()
.antMatchers("/**").authenticated() // These urls are allowed by any authenticated user
.and()
.httpBasic();
http.csrf().disable();
}
#Bean
public UserDetailsService userDetailsService() {
log.info("Setting in-memory security using the user input...");
String username = null;
String password = null;
System.out.println("\nPlease set the admin credentials for this web application (will be required when browsing to the web application)");
Console console = System.console();
// Read the credentials from the user console:
// Note:
// Console supports password masking, but is not supported in IDEs such as eclipse;
// thus if in IDE (where console == null) use scanner instead:
if (console == null) {
// Use scanner:
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("Username: ");
username = scanner.nextLine();
System.out.print("Password: ");
password = scanner.nextLine();
System.out.print("Confirm Password: ");
String inputPasswordConfirm = scanner.nextLine();
if (username.isEmpty()) {
System.out.println("Error: user must be set - please try again");
} else if (password.isEmpty()) {
System.out.println("Error: password must be set - please try again");
} else if (!password.equals(inputPasswordConfirm)) {
System.out.println("Error: password and password confirm do not match - please try again");
} else {
log.info("Setting the in-memory security using the provided credentials...");
break;
}
System.out.println("");
}
scanner.close();
} else {
// Use Console
while (true) {
username = console.readLine("Username: ");
char[] passwordChars = console.readPassword("Password: ");
password = String.valueOf(passwordChars);
char[] passwordConfirmChars = console.readPassword("Confirm Password: ");
String passwordConfirm = String.valueOf(passwordConfirmChars);
if (username.isEmpty()) {
System.out.println("Error: Username must be set - please try again");
} else if (password.isEmpty()) {
System.out.println("Error: Password must be set - please try again");
} else if (!password.equals(passwordConfirm)) {
System.out.println("Error: Password and Password Confirm do not match - please try again");
} else {
log.info("Setting the in-memory security using the provided credentials...");
break;
}
System.out.println("");
}
}
// Set the inMemoryAuthentication object with the given credentials:
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
if (username != null && password != null) {
String encodedPassword = passwordEncoder().encode(password);
manager.createUser(User.withUsername(username).password(encodedPassword).roles("USER").build());
}
return manager;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Related
I want to rewrite my Vaadin application to Vaadin 21.
With the Vaadin starter builder (https://vaadin.com/start) I created a simple app.
Currently my main struggle is to apply my simple CustomAuthenticationProvider to the Security manager to able to use the #RolesAllowed({ "user", "admin","USER"}) annotation.
Main problem that my AuthToken is generated somewhere else...
Its generate somewhere an empty Granted Authrities and ignore my custom AuthProvider code.
Question:
How to nicely handle role based access control?
Where I can use this annotation correctly:
#RolesAllowed({ "user", "admin","USER"})
public class ProfileView extends VerticalLayout {
Console after login:
UsernamePasswordAuthenticationToken [Principal=c.farkas, Credentials=[PROTECTED], Authenticated=false, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=DDE103F559B2F64B917753636B800564], Granted Authorities=[]]
xxx[USERcica, admin, USER]
??UsernamePasswordAuthenticationToken [Principal=c.farkas, Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[USERcica, admin, USER]]
SecurityConfiguration.java
#EnableWebSecurity
#Configuration
public class SecurityConfiguration extends VaadinWebSecurityConfigurerAdapter {
#Autowired
private RequestUtil requestUtil;
#Autowired
private VaadinDefaultRequestCache vaadinDefaultRequestCache;
#Autowired
private ViewAccessChecker viewAccessChecker;
#Autowired
CustomAuthenticationProvider customAuthenticationProvider;
public static final String LOGOUT_URL = "/";
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// super.configure(http);
http.csrf().ignoringRequestMatchers(requestUtil::isFrameworkInternalRequest);
// nor with endpoints
http.csrf().ignoringRequestMatchers(requestUtil::isEndpointRequest);
// Ensure automated requests to e.g. closing push channels, service
// workers,
// endpoints are not counted as valid targets to redirect user to on
// login
http.requestCache().requestCache(vaadinDefaultRequestCache);
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry urlRegistry = http
.authorizeRequests();
// Vaadin internal requests must always be allowed to allow public Flow
// pages
// and/or login page implemented using Flow.
urlRegistry.requestMatchers(requestUtil::isFrameworkInternalRequest).permitAll();
// Public endpoints are OK to access
urlRegistry.requestMatchers(requestUtil::isAnonymousEndpoint).permitAll();
// Public routes are OK to access
urlRegistry.requestMatchers(requestUtil::isAnonymousRoute).permitAll();
urlRegistry.requestMatchers(getDefaultHttpSecurityPermitMatcher()).permitAll();
// all other requests require authentication
urlRegistry.anyRequest().authenticated();
// Enable view access control
viewAccessChecker.enable();
setLoginView(http, LoginView.class, LOGOUT_URL);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Custom authentication provider - Order 1
auth.authenticationProvider(customAuthenticationProvider);
// Built-in authentication provider - Order 2
/* auth.inMemoryAuthentication().withUser("admin").password("{noop}admin#password")
// {noop} makes sure that the password encoder doesn't do anything
.roles("ADMIN") // Role of the user
.and().withUser("user").password("{noop}user#password").credentialsExpired(true).accountExpired(true)
.accountLocked(true).roles("USER");*/
}
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.ignoring().antMatchers("/images/*.png");
}
}
CustomAuthenticationProvider.java
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
System.out.println(authentication);
try {
// LdapContext ldapContext =
ActiveDirectory.getConnection(username, password);
List<GrantedAuthority> authorityList = new ArrayList<GrantedAuthority>();
authorityList.add(new SimpleGrantedAuthority("USER" + "cica"));
authorityList.add(new SimpleGrantedAuthority("admin"));
authorityList.add(new SimpleGrantedAuthority("USER"));
System.out.println("xxx"+authorityList.toString());
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
username, password, authorityList);
System.out.println("??" + usernamePasswordAuthenticationToken);
String id = VaadinSession.getCurrent() != null ? VaadinSession.getCurrent().getSession().getId() : "";
return usernamePasswordAuthenticationToken;
} catch (NamingException e) {
// e.printStackTrace();
// throw new CortexException("Authentication failed");
throw new BadCredentialsException("Authentication failed");
}
}
#Override
public boolean supports(Class<?> aClass) {
return aClass.equals(UsernamePasswordAuthenticationToken.class);
}
}
You must add the ROLE_ prefix to tell Spring Security that the GrantedAuthority is of type role.
authorityList.add(new SimpleGrantedAuthority("ROLE_USER" + "cica"));
authorityList.add(new SimpleGrantedAuthority("ROLE_admin"));
authorityList.add(new SimpleGrantedAuthority("ROLE_USER"));
I have problem with validating user credentials. When I give correct credentials first time everything goes OK but giving invalid credentials first and then give correct ones I get invalid credentials error. I use Postman Basic
Auth.
My config class:
#Configuration
#EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Autowired
private CustomAuthenticationEntryPoint authenticationEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST ,"/login").permitAll()
.antMatchers("/admin").hasAuthority("ADMIN")
.anyRequest().authenticated().and().exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS).and()
.logout()
.deleteCookies("remove")
.invalidateHttpSession(true);
http.rememberMe().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.userService)
.and().eraseCredentials(true);
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
And my controller class
#PostMapping
public ResponseEntity<?> loginButtonClicked(HttpServletRequest request) {
HttpSession session = request.getSession();
final String authorization = request.getHeader("Authorization");
String[] authorizationData=null;
if (authorization != null && authorization.startsWith("Basic")) {
// Authorization: Basic base64credentials
String base64Credentials = authorization.substring("Basic" .length()).trim();
String credentials = new String(Base64.getDecoder().decode(base64Credentials),
Charset.forName("UTF-8"));
// credentials = username:password
authorizationData = credentials.split(":", 2);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(authorizationData[0], authorizationData[1],Arrays.asList(new SimpleGrantedAuthority("USER")));
User user = userService.findUserEntityByLogin(authorizationData[0]);
if(user != null && user.getFromWhenAcceptLoginAttempts() != null && (user.getFromWhenAcceptLoginAttempts()).isBefore(LocalDateTime.now())){
// Authenticate the user
Authentication authentication = authenticationManager.authenticate(authRequest);
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(authentication);
// Create a new session and add the security context.
session = request.getSession();
session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
return new ResponseEntity<>(new LoginResponseObject(200,"ACCESS GRANTED. YOU HAVE BEEN AUTHENTICATED"), HttpStatus.OK);
}else{
session.getId();
SecurityContextHolder.clearContext();
if(session != null) {
session.invalidate();
}
return new ResponseEntity<>(new ErrorObject(403,"TOO MANY LOGIN REQUESTS","YOU HAVE ENTERED TOO MANY WRONG CREDENTIALS. YOUR ACCOUNT HAS BEEN BLOCKED FOR 15 MINUTES.", "/login"), HttpStatus.FORBIDDEN);
}
}else{
session.getId();
SecurityContextHolder.clearContext();
if(session != null) {
session.invalidate();
}
return new ResponseEntity<>(new ErrorObject(401,"INVALID DATA","YOU HAVE ENTERED WRONG USERNAME/PASSWORD CREDENTIALS", "/login"), HttpStatus.UNAUTHORIZED);
}
}
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public ObjectMapper objectMapper(){
return new ObjectMapper();
}
#Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
The problem is that the request is stored in cache due to your sessionCreationPolicy.
To avoid this problem, you could add .requestCache().requestCache(new NullRequestCache()) in your http security config to override the default request cache configuration, but be careful because this could create another side effect (it depends on your application).
In case you do not need the session, you can choose another session policy:
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).
Another alternative is to relay in Spring's BasicAuthenticationFilter. This filter does all the authentication logic for you. To enable it, you only have to add .httpBasic()in your http security configuration.
You may want to add a custom logic on authentication success/failure. In that case, you only have to create a custom filter (CustomBasicAuthenticationFilter) that extends BasicAuthenticationFilter class and overrides the methods onSuccessfulAuthentication()and onUnsuccessfulAuthentication(). You will not need to add .httpBasic() but you will need to insert your custom filter in the correct place:
.addFilterAfter(new CustomBasicAuthenticationFilter(authenticationManager), LogoutFilter.class).
Any of that 3 solutions will avoid your problem.
Try to write .deleteCookies("JSESSONID") in your SpringSecurityConfig class.
while experimenting around with spring boot, security, and data.
i just came across this scenario:
i use H2 in memory DB and poblate it with one user with liquibase on startup
with username and password.
now i want spring security to authenticate against H2. for that purpose i have this code:
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsServiceImp);
}
and im implementing the userDetails as follows:
#Override
public UserDetails loadUserByUsername(String username) {
//this works, the user with pass is pulled
com.fix.demo.logic.user.User byUsername =
userRepository.findByUsername(username);
if (byUsername == null) {
System.out.println("No user found with username: ");
return null; //trow ex here
}
User user = new User(byUsername.getUsername(),
byUsername.getPassword(), true, true,
true, true, getAuthorities(Collections.singletonList("user")));
//System.out.println(user.toString());
//System.out.println(byUsername.toString()+ " "+byUsername.getPassword());
return user;
}
but my tests keep failing with
Authentication should not be null
and trying to log in will give me
bad credentials
what is necessary for my custom implementation of UserDetailsService to work?
this is the failing test:
#Test
public void loginWithValidUserThenAuthenticated() throws Exception {
FormLoginRequestBuilder login = formLogin()
.user("admin")
.password("root");
mockMvc.perform(login)
.andExpect(authenticated().withUsername("admin"));
}
One of the reasons is, the password might my encoded and you need to tell spring security to use an encoder. Add the following line to the configure override.
auth.setPasswordEncoder(passwordEncoder());
define the passwordEncoder bean.
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
When I deploy my Spring application via Spring Boot and access localhost:8080 I have to authenticate, but what is the username and password or how can I set it? I tried to add this to my tomcat-users file but it didn't work:
<role rolename="manager-gui"/>
<user username="admin" password="admin" roles="manager-gui"/>
This is the starting point of the application:
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
And this is the Tomcat dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
How do I authenticate on localhost:8080?
I think that you have Spring Security on your class path and then spring security is automatically configured with a default user and generated password
Please look into your pom.xml file for:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
If you have that in your pom than you should have a log console message like this:
Using default security password: ce6c3d39-8f20-4a41-8e01-803166bb99b6
And in the browser prompt you will import the user user and the password printed in the console.
Or if you want to configure spring security you can take a look at Spring Boot secured example
It is explained in the Spring Boot Reference documentation in the Security section, it indicates:
The default AuthenticationManager has a single user (‘user’ username and random password, printed at `INFO` level when the application starts up)
Using default security password: 78fa095d-3f4c-48b1-ad50-e24c31d5cf35
If spring-security jars are added in classpath and also if it is spring-boot application all http endpoints will be secured by default security configuration class SecurityAutoConfiguration
This causes a browser pop-up to ask for credentials.
The password changes for each application restarts and can be found in console.
Using default security password: 78fa095d-3f4c-48b1-ad50-e24c31d5cf35
To add your own layer of application security in front of the defaults,
#EnableWebSecurity
public class SecurityConfig {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
or if you just want to change password you could override default with,
application.xml
security.user.password=new_password
or
application.properties
spring.security.user.name=<>
spring.security.user.password=<>
When overriding
spring.security.user.name=
spring.security.user.password=
in application.properties, you don't need " around "username", just use username. Another point, instead of storing raw password, encrypt it with bcrypt/scrypt and store it like
spring.security.user.password={bcrypt}encryptedPassword
If you can't find the password based on other answers that point to a default one, the log message wording in recent versions changed to
Using generated security password: <some UUID>
You can also ask the user for the credentials and set them dynamically once the server starts (very effective when you need to publish the solution on a customer environment):
#EnableWebSecurity
public class SecurityConfig {
private static final Logger log = LogManager.getLogger();
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
log.info("Setting in-memory security using the user input...");
Scanner scanner = new Scanner(System.in);
String inputUser = null;
String inputPassword = null;
System.out.println("\nPlease set the admin credentials for this web application");
while (true) {
System.out.print("user: ");
inputUser = scanner.nextLine();
System.out.print("password: ");
inputPassword = scanner.nextLine();
System.out.print("confirm password: ");
String inputPasswordConfirm = scanner.nextLine();
if (inputUser.isEmpty()) {
System.out.println("Error: user must be set - please try again");
} else if (inputPassword.isEmpty()) {
System.out.println("Error: password must be set - please try again");
} else if (!inputPassword.equals(inputPasswordConfirm)) {
System.out.println("Error: password and password confirm do not match - please try again");
} else {
log.info("Setting the in-memory security using the provided credentials...");
break;
}
System.out.println("");
}
scanner.close();
if (inputUser != null && inputPassword != null) {
auth.inMemoryAuthentication()
.withUser(inputUser)
.password(inputPassword)
.roles("USER");
}
}
}
(May 2018) An update - this will work on spring boot 2.x:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger log = LogManager.getLogger();
#Override
protected void configure(HttpSecurity http) throws Exception {
// Note:
// Use this to enable the tomcat basic authentication (tomcat popup rather than spring login page)
// Note that the CSRf token is disabled for all requests
log.info("Disabling CSRF, enabling basic authentication...");
http
.authorizeRequests()
.antMatchers("/**").authenticated() // These urls are allowed by any authenticated user
.and()
.httpBasic();
http.csrf().disable();
}
#Bean
public UserDetailsService userDetailsService() {
log.info("Setting in-memory security using the user input...");
String username = null;
String password = null;
System.out.println("\nPlease set the admin credentials for this web application (will be required when browsing to the web application)");
Console console = System.console();
// Read the credentials from the user console:
// Note:
// Console supports password masking, but is not supported in IDEs such as eclipse;
// thus if in IDE (where console == null) use scanner instead:
if (console == null) {
// Use scanner:
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("Username: ");
username = scanner.nextLine();
System.out.print("Password: ");
password = scanner.nextLine();
System.out.print("Confirm Password: ");
String inputPasswordConfirm = scanner.nextLine();
if (username.isEmpty()) {
System.out.println("Error: user must be set - please try again");
} else if (password.isEmpty()) {
System.out.println("Error: password must be set - please try again");
} else if (!password.equals(inputPasswordConfirm)) {
System.out.println("Error: password and password confirm do not match - please try again");
} else {
log.info("Setting the in-memory security using the provided credentials...");
break;
}
System.out.println("");
}
scanner.close();
} else {
// Use Console
while (true) {
username = console.readLine("Username: ");
char[] passwordChars = console.readPassword("Password: ");
password = String.valueOf(passwordChars);
char[] passwordConfirmChars = console.readPassword("Confirm Password: ");
String passwordConfirm = String.valueOf(passwordConfirmChars);
if (username.isEmpty()) {
System.out.println("Error: Username must be set - please try again");
} else if (password.isEmpty()) {
System.out.println("Error: Password must be set - please try again");
} else if (!password.equals(passwordConfirm)) {
System.out.println("Error: Password and Password Confirm do not match - please try again");
} else {
log.info("Setting the in-memory security using the provided credentials...");
break;
}
System.out.println("");
}
}
// Set the inMemoryAuthentication object with the given credentials:
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
if (username != null && password != null) {
String encodedPassword = passwordEncoder().encode(password);
manager.createUser(User.withUsername(username).password(encodedPassword).roles("USER").build());
}
return manager;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Addition to accepted answer -
If password not seen in logs, enable "org.springframework.boot.autoconfigure.security" logs.
If you fine-tune your logging configuration, ensure that the
org.springframework.boot.autoconfigure.security category is set to log
INFO messages, otherwise the default password will not be printed.
https://docs.spring.io/spring-boot/docs/1.4.0.RELEASE/reference/htmlsingle/#boot-features-security
For a start simply add the following to your application.properties file
spring.security.user.name=user
spring.security.user.password=pass
NB: with no double quote
Run your application and enter the credentials (user, pass)
As of Spring Security version 5.7.1, the default username is user and the password is randomly generated and displayed in the console (e.g. 8e557245-73e2-4286-969a-ff57fe326336).
Please see the documentation for further details:
https://docs.spring.io/spring-security/reference/servlet/getting-started.html
When I started learning Spring Security, then I overrided the method userDetailsService() as in below code snippet:
#Configuration
#EnableWebSecurity
public class ApplicationSecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/index").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
}
#Override
#Bean
public UserDetailsService userDetailsService() {
List<UserDetails> users= new ArrayList<UserDetails>();
users.add(User.withDefaultPasswordEncoder().username("admin").password("nimda").roles("USER","ADMIN").build());
users.add(User.withDefaultPasswordEncoder().username("Spring").password("Security").roles("USER").build());
return new InMemoryUserDetailsManager(users);
}
}
So we can log in to the application using the above-mentioned creds. (e.g. admin/nimda)
Note: This we should not use in production.
Try to take username and password from below code snipet in your project and login and hope this will work.
#Override
#Bean
public UserDetailsService userDetailsService() {
List<UserDetails> users= new ArrayList<UserDetails>();
users.add(User.withDefaultPasswordEncoder().username("admin").password("admin").roles("USER","ADMIN").build());
users.add(User.withDefaultPasswordEncoder().username("spring").password("spring").roles("USER").build());
return new UserDetailsManager(users);
}
I am implementing spring Security in a Java EE application (Spring / Struts / Hibernate). I have some truble with my custum DaoAuthenticationProvider.
#Override
public void configure(AuthenticationManagerBuilder pAuth) throws Exception {
pAuth.authenticationProvider(mAuthenticationProvider)
.userDetailsService(mUserDetailsService)
.passwordEncoder(new Md5PasswordEncoder());
}
This is in my SecurityConfig (extends WebSecurityConfigurerAdapter) class.
When I debug the app, I can see that in my custom DaoAuthenticationProvider the password encoder is not set (PlainTextPasswordEncoder instead of Md5), why ?
After that I tried to set manually in the constructor this values :
public LimitLoginAuthenticationProvider() {
setPasswordEncoder(new Md5PasswordEncoder());
setUserDetailsService(mUserDetailsService);
}
When I debug it I see the right values.
But in both cases, if I do :
#Override
public Authentication authenticate(Authentication pAuthentication) {
Authentication lAuth = super.authenticate(pAuthentication);
return lAuth;
}
The property of lAuth that indicate if user is authenticate or not is at true whatever the password is...
Does anyone have the answer ?
EDIT : LimitLoginAuthenticationProvider implementation
#Component("authenticationProvider")
public class LimitLoginAuthenticationProvider extends DaoAuthenticationProvider {
#Autowired
private IUserDao mUserDao;
#Autowired
#Qualifier("userDetailsService")
UserDetailsService mUserDetailsService;
public LimitLoginAuthenticationProvider() {
setPasswordEncoder(new Md5PasswordEncoder());
}
#Autowired
#Qualifier("userDetailsService")
#Override
public void setUserDetailsService(UserDetailsService userDetailsService) {
super.setUserDetailsService(userDetailsService);
}
#Override
public Authentication authenticate(Authentication pAuthentication) {
Authentication lAuth = super.authenticate(pAuthentication);
return lAuth;
}
#Override
#Transactional(readOnly = true)
protected void additionalAuthenticationChecks(UserDetails pUserDetails,
UsernamePasswordAuthenticationToken pAuthentication)
throws AuthenticationException {
try {
User lUser = mUserDao.findUserByLogin(pAuthentication.getName());
if (lUser.getStatus() >= 3) {
logger.debug("User account is locked");
throw new LockedException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.locked",
"User account is locked"));
}
} catch (DaoException e) {
}
}
}
Ok I think I missunderstood the objective of DaoAuthenticationProvider.
I think I have to check the password myself :
PasswordEncoder lPasswordEncoder = getPasswordEncoder();
if (!lPasswordEncoder.isPasswordValid(lUser.getPassword(),
pAuthentication.getCredentials().toString(), null)) {
throw new BadCredentialsException("Wrong password for user "
+ lUser.getLogin());
}
(am I wrong ?)