Configuration of WebSecurityConfigurerAdapter for log in - java

I'm trying to configure spring secutiry in my SpringBoot 2 application.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/registration").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.defaultSuccessUrl("/users")
.and()
.logout().permitAll();
}
#Override
public UserDetailsService userDetailsService() {
UserDetails user =
User.withDefaultPasswordEncoder()
.username("u")
.password("p")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
#Configuration
public class MvcConfig implements WebMvcConfigurer {
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
}
login.mustache
<form method="post" action="/login">
<input type="text" name="username" id="username" placeholder="Login" /><br>
<input type="password" name="password" id="password" placeholder="Password" /><br>
<input type="hidden" name="_csrf" value="{{_csrf.token}}">
<button type="submit">Login</button>
</form>
I expect to be redirected to /users page. But actually I get error code = 302 and I appear at /login/error
SOLUTION: I've added #Bean annotation to userDetailsService() and it helped.

You have to expose your UserDetailsService as a bean, see Spring Boot Reference:
84.2 Change the AuthenticationManager and Add User Accounts
If you provide a #Bean of type AuthenticationManager, AuthenticationProvider, or UserDetailsService, the default #Bean for InMemoryUserDetailsManager is not created, so you have the full feature set of Spring Security available (such as various authentication options).
The easiest way to add user accounts is to provide your own UserDetailsService bean.
Your modified code:
#Bean
#Override
public UserDetailsService userDetailsService() {
UserDetails user =
User.withDefaultPasswordEncoder()
.username("u")
.password("p")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}

Related

After registration page user isn't saved on DB Springboot

I just want to register a user on my web site using springboot and thymeleaf, the problem is that when user clicks on "submit", after filled the registration form to save its credentials, well this operation isn't done and I land on login page.
I'm sure registration isn't completed because when I try to login it is unsuccessful and both "credentials" and "user" tables are empty.
This is the registration form, I'm saving "user" and "credentials" two different entities :
<form th:action="#{/process_register}" method="post">
<label for="username">Email:</label>
<input type="email" name="username" id="username" required th:field="${credentials.username}"/>
<label for="password">Password:</label>
<input type="password" name="password" id="password" required th:field="${credentials.password}"/>
<label for="name">Nome:</label>
<input type="name" name="name" id="name" th:field="${user.name}" required />
<button type="submit" class="btn btn-primary">Register</button>
<a th:href="#{/login}" href="login.html" > or login</a>
</form>
This is /process_register controller :
#PostMapping("/process_register")
public String processRegister(#ModelAttribute("credentials")Credentials credentials,#ModelAttribute("user") User user) {
credentials.setUser(user);
credentialService.saveCredentials(credentials);
System.out.println("Ho invocato saveCredentials");
return "login";
}
saveCredentials() method :
#Transactional
public Credentials saveCredentials(Credentials credentials) {
credentials.setPassword(this.passwordEncoder.encode(credentials.getPassword()));
return this.credentialsRepository.save(credentials);
}
where credentialsRepository extends CrudRepository.
EDIT :
AuthConfiguration :
#Configuration
#EnableWebSecurity
public class AuthConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
DataSource datasource;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// authorization paragraph: qui definiamo chi può accedere a cosa
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/",
"/index",
"/login",
"/collection/*",
"/collections",
"/NFTS",
"/signup_form",
"/register",
"/css/**",
"zzz",
"/images/**").permitAll()
.antMatchers(HttpMethod.POST, "/login", "/register").permitAll()
.antMatchers(HttpMethod.GET, "/admin/**").hasAnyAuthority(ADMIN_ROLE)
.antMatchers(HttpMethod.POST, "/admin/**").hasAnyAuthority(ADMIN_ROLE)
.anyRequest().authenticated()
.and().formLogin()
.loginPage("/login")
.defaultSuccessUrl("/default")
.and().logout()
.logoutUrl("/logout")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/index")
.invalidateHttpSession(true)
.clearAuthentication(true).permitAll();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
//use the autowired datasource to access the saved credentials
.dataSource(this.datasource)
//retrieve username and role
.authoritiesByUsernameQuery("SELECT username, role FROM credentials WHERE username=?")
//retrieve username, password and a boolean flag specifying whether the user is enabled or not (always enabled in our case)
.usersByUsernameQuery("SELECT username, password, 1 as enabled FROM credentials WHERE username=?");
/*auth.inMemoryAuthentication()
.withUser("user1").password(passwordEncoder().encode("user1")).roles("USER")
.and()
.withUser("user2").password(passwordEncoder().encode("user2")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder().encode("admin")).roles("ADMIN_ROLE");*/
}
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
After looking at github repo: you need to create your controllers with regard to auth filters configured in AuthConfiguration. Only a few paths are allowed to be accessed by an unauthorized user i.a. /register and /login.
Since /process_register is not whitelisted, then the POST request doesn't reach the controller. It is caugth by the filter and redirected to login page instead.
To fix the issue you can for instance do these:
Change #PostMapping("/process_register") to #PostMapping("/register") in your controller.
Change th:action="#{/process_register}" to th:action="#{/register}" in your template.
After it's done the registration form should work.
By the way:
You should not use th:field like this. It is supposed to be used together with form backing bean (th:object) - please refer to the documentation.

Spring Boot login login returns 401 with matching credentials

I have a Spring Boot application which is able to login and register new users. My users are saved in a database (so far, I use in-memory H2 databse for testing).
The service is secured with OAuth2 (it is an auth server for other services I am developing).
Everything works fine and I am able to check the user's credentials but I would like to redirect the user somewhere after successful login. It works if I access the login page with the response_type=token parameter.
http://localhost:9000/auth/oauth/authorize?response_type=token&client_id=client&redirect_uri=http://localhost:9000/auth/user
But when I access the login page directly and login, it redirects me to the page I selected as my default success page but without the token or anything else that would indicate that the user is logged in and I get 401. Going straight to the /login and using the correct credentials results in this:
<oauth>
<error_description>
Full authentication is required to access this resource
</error_description>
<error>unauthorized</error>
</oauth>
Could someone point out to me what I need to adjust to make the login work?
WebSecurityConfig:
#Configuration
#Order(-20)
#EnableResourceServer
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("userDetailsService")
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests()
.antMatchers("/register", "/confirm").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/")
.permitAll()
.and()
.requestMatchers()
.antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access", "/register", "/confirm")
.and()
.logout()
.permitAll();
// #formatter:on
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager);
auth.userDetailsService(userDetailsService);
}
}
OAuth2Config:
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("userDetailsService")
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private TokenStore tokenStore;
#Autowired
private DataSource dataSource;
// password encryptor
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer configurer) throws Exception {
configurer.authenticationManager(authenticationManager);
configurer.userDetailsService(userDetailsService);
configurer.tokenStore(tokenStore);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
}
LoginController:
#Controller
public class LoginController {
#GetMapping("/login")
public String login() {
return "login";
}
}
Login page:
<body>
<div class="container">
<form class="form-signin" th:action="#{/login}" method="post">
<h2 class="form-signin-heading">Sign in</h2>
<div th:if="${param.error}">Invalid username and password.</div>
<div th:if="${param.logout}">You have been logged out.</div>
<label for="inputEmail" class="sr-only">Email</label>
<input type="text" id="username"
class="form-control" placeholder="Email" name="username" th:required="required" th:autofocus="autofocus" />
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" id="inputPassword"
class="form-control" placeholder="Password" name="password" th:required="required" />
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
</div>
<!-- /container -->
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
Thank you for any help. I am happy to provide more details if this is not enough.
EDIT: What I want is basically a steteless authentication between the Auth server and other microservices but sessions-based login on the Auth server itself.
Check this first:
Spring Security OAuth 2 with form login
Provided that an access token is being created with basic form login, I would add custom authentication success handler:
WebSecurityConfig:
#Autowired
private AuthenticationSuccessHandler myAuthenticationSuccessHandler;
//
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/")
.successHandler(myAuthenticationSuccessHandler)
And the handler "MySimpleUrlAuthenticationSuccessHandler":
#Component("myAuthenticationSuccessHandler")
public class MySimpleUrlAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
//logic to grant access
}

Spring Security java configuration and login form

I've read about spring security also seen some examples but I can't make it work... I'm not sure if I'm missing something. Please I'll appreciate some explication because I'm getting hard time to understand this.
Using Spring mvc 4.3.3, Spring Security 4.2.0, Tiles 3, CSS, Java 1.7, Eclipse neon.
1.- My first page is login page (I don't use a home page or index).
2.- I want Spring Security take user and pass from my login (first page shown in browser), also I'm using <form action="<c:url value='j_spring_security_check' />" method="post"> in my login but something is wrong.
3.- I want it to redirect to the same view to all users /myPanel (I'll change menus acording to user role)
Structure;
Classes (removed imports and packages); UPDATE:
ApplicationContextConfig.java
#Configuration
#ComponentScan("mx.com.myapp.*")
#Import({ SecurityConfig.class })
public class ApplicationContextConfig {
#Bean(name = "viewResolver")
public ViewResolver getViewResolver() {
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
// TilesView 3
viewResolver.setViewClass(TilesView.class);
return viewResolver;
}
#Bean(name = "tilesConfigurer")
public TilesConfigurer getTilesConfigurer() {
TilesConfigurer tilesConfigurer = new TilesConfigurer();
// TilesView 3
tilesConfigurer.setDefinitions("/WEB-INF/tiles.xml");
return tilesConfigurer;
}
WebMvcConfig.java:
#Configuration
//#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
// #Override
// public void addResourceHandlers(ResourceHandlerRegistry registry) {
//
// // Default..
// }
//
// #Override
// public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
// configurer.enable();
// }
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
}
SpringWebAppInitializer.java
public class SpringWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(ApplicationContextConfig.class);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("SpringDispatcher",
new DispatcherServlet(appContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
// UtF8 Charactor Filter.
FilterRegistration.Dynamic fr = servletContext.addFilter("encodingFilter", CharacterEncodingFilter.class);
fr.setInitParameter("encoding", "UTF-8");
fr.setInitParameter("forceEncoding", "true");
fr.addMappingForUrlPatterns(null, true, "/*");
}
}
SpringSecurityInitializer.java
public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}
SecurityConfig.java
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("mkyong").password("123456").roles("ADMIN");
System.out.println("SpringSecurity ConfigureGlobal");
}
// .csrf() is optional, enabled by default, if using WebSecurityConfigurerAdapter constructor
// #Override
// protected void configure(HttpSecurity http) throws Exception {
//
// System.out.println("SpringSecurity configure");
// http.authorizeRequests()
// .antMatchers("/").permitAll()
// .antMatchers("/myPanel**").access("hasRole('ADMIN')")
// .and().formLogin()
// .usernameParameter("username").passwordParameter("password")
// .permitAll()
// .and()
// .csrf();
// }
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated().and().formLogin()
.loginPage("/login").failureUrl("/login?error").permitAll().and()
.logout().permitAll();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/path/**");
}
}
MyController.java
#Controller
public class MyController {
#RequestMapping(value = { "/" })
public String loginPage(Model model) {
return "loginPage";
}
#RequestMapping(value = { "/myPanel" }, method = RequestMethod.POST)
public ModelAndView myPanel(HttpServletRequest request, HttpServletResponse response) {
System.out.println("INICIA REQUEST");
System.out.println("-------- " + request.getParameter("user"));
String message = "<br><div style='text-align:center;'>"
+ "<h3>********** This is protected page!</h3> **********</div><br><br>";
System.out.println("TERMINA REQUEST");
return new ModelAndView("homePage", "message", message);
}
//Spring Security see this :
#RequestMapping(value = "/login", method = RequestMethod.POST)
public ModelAndView login(
#RequestParam(value = "error", required = false) String error,
#RequestParam(value = "logout", required = false) String logout) {
System.out.println("/login SpringSecurity");
ModelAndView model = new ModelAndView();
if (error != null) {
model.addObject("error", "Invalid username and password!");
}
if (logout != null) {
model.addObject("msg", "You've been logged out successfully.");
}
model.setViewName("homePage");
return model;
}
}
login.jsp
<form action="<c:url value='/login' />" method="post">
<c:if test="${not empty error}">
<div class="error">${error}</div>
</c:if>
<c:if test="${not empty msg}">
<div class="msg">${msg}</div>
</c:if>
<input type="text" name="username" placeholder="Username" required="required" class="input-txt" />
<input type="password" name="password" placeholder="Password" required="required" class="input-txt" />
<div class="login-footer">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<strong>I've forgotten something |
Register</strong>
<button type="submit" class="btn btn--right">Sign in</button>
</div>
</form>
Many thanks in advance.
Your missing permit all on the login page -
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("SpringSecurity configure");
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/myPanel**").access("hasRole('ADMIN')")
.and().formLogin()
.usernameParameter("username").passwordParameter("password")
.permitAll()
.and()
.csrf();
}
It seems that your setup is quite over-complicated, try to simplify it.
Spring security config:
#Configuration
public static class WebFormSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated().and().formLogin()
.loginPage("/login").failureUrl("/login?error").permitAll().and()
.logout().permitAll();
}
}
Ignore public resources
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/path/**");
}
And MVC config, you don't have to have implementation of /login action:
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
}

SpringSecurity error on Wildfly

While log in with spring-security on wildfly i get this error-page:
{"timestamp":1464679377206,"status":999,"error":"None","message":"No message available"}
after refresh it redirects me on my custom error page. Then if I clear error-link (like that http://myapp/error -> http://myapp) application works correctly. When I launch this app with spring-boot (not wildfly) there is no such problem.
Application class
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(applicationClass);
}
private static Class<Application> applicationClass = Application.class;
}
Thymeleaf login form
<div sec:authorize="isAnonymous()" id="anonymous-navbar" class="navbar-collapse collapse">
<form th:action="#{/login}" method="post" class="navbar-form navbar-right">
<div class="form-group">
<input type="text" name="username" placeholder="User" class="form-control" />
</div>
<div class="form-group">
<input type="password" name="password" placeholder="Password" class="form-control" />
</div>
<button type="submit" class="btn btn-success" value="login">Log in</button>
</form>
Security configuration
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserService userService;
#Autowired
#Qualifier("userServiceImpl")
UserDetailsService userDetailsService;
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home", "/signup", "/add_person").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/images/**");
}
}
After debugging spring class DefaultErrorAttributes, which sends the message I got this error
FirewalledRequest[ HttpServletRequestImpl [ GET /PersonalFinance/error ]]
I've watched wildfly logs and haven't found anything useful.
There are some same questions:
Spring Security with basic auth redirecting to /error for invalid credentials
Spring Security - Remember Me Authentication Error
But these answers haven't solved the problem
Error occurred cause folder with images was not included to project, but was specified in spring-security configuration. Thus spring didn't find this path and threw an error.

Spring Security login with UserDetailsService and Java config

I am trying to add login functionality with a database lookup with Spring Security, and I am really struggling. I want to look up the user by implementing the UserDetailsService interface. I have read a ton of documentation and Googled for hours, but I am still stuck. Most of the examples that I could find are using XML, so maybe my problems are related to converting those to Java configuration.
UserDetailsService
#Service
public class AccountServiceImpl implements AccountService { // AccountService implements UserDetailsService
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
// The email variable is always ''
List<GrantedAuthority> authList = new ArrayList<>();
authList.add(new Role("ROLE_USER")); // Role implements GrantedAuthority
return new User("test#example.com", "password", true, true, true, true, authList);
}
}
Security config
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private LogoutSuccessHandler logoutSuccessHandler;
#Autowired
private AccountService accountService;
#Override
#Bean
protected AuthenticationManager authenticationManager() throws Exception {
// This bean is required for using method security annotations
return super.authenticationManager();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login/process")
.failureUrl("/login?error=true")
.defaultSuccessUrl("/", false)
.and()
//.userDetailsService(this.accountService)
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(this.logoutSuccessHandler)
.invalidateHttpSession(true)
.and()
// Permissions here
.csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("test#example.com").password("password").roles("USER");
}
/*#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
//.userDetailsService(this.accountService);
.inMemoryAuthentication()
.withUser("test#example.com").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}*/
/*#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(this.accountService);
return provider;
}*/
}
As you can see, there is a bunch of outcommented code, which are just some of the things I tried to make it work. I also tried with simple in-memory authentication, but that didn't work either.
login.jsp
<form action="/login/process" method="POST">
<input name="j_username" id="j_username" type="text" />
<input name="j_password" id="j_password" type="password" />
<input type="submit" value="Login" />
</form>
Debug
DEBUG org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Request is to process authentication
DEBUG org.springframework.security.authentication.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
DEBUG org.springframework.security.authentication.dao.DaoAuthenticationProvider - User '' not found
DEBUG org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
DEBUG org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Updated SecurityContextHolder to contain null Authentication
The above seems to always be the case for all of the configurations that I tried. I am not even sure why the DaoAuthenticationProvider is used, even if I try to override it; I guess it's a default. For some of the configuration above, my UserDetailsService implementation gets called, but with an empty string as the parameter. Also, even if I return a hard coded UserDetails object with the same credentials as the ones I entered in my form, the authentication still fails, and I get redirected back to /login?error=true.
What am I doing wrong here? Please inform me about my mistakes or provide a simple example using Java configuration. Thanks!
When using Spring Security with Java based configuration the name of the request parameters are username and password and not j_username and j_password anymore.
You can either fix your login form
<form action="/login/process" method="POST">
<input name="username" id="j_username" type="text" />
<input name="password" id="j_password" type="password" />
<input type="submit" value="Login" />
</form>
or adapt your configuration to use the old field names.
formLogin().usernameParameter("j_username").passwordParameter("j_password");
Either way will resolve your issue.

Categories

Resources