I have implemented Spring Security to my project, but I am getting status 405 when I try to log in. I have already added csrf token in the form.
This is the error I am getting when I send username and password:
HTTP Status 405 - Request method 'POST' not supported
Spring version: 4.0.2.RELEASED
<div class="login-form">
<c:url var="loginUrl" value="/login" />
<form action="${loginUrl}" method="post" class="form-horizontal">
<c:if test="${param.error != null}">
<div class="alert alert-danger">
<p>Invalid username and password.</p>
</div>
</c:if>
<c:if test="${param.logout != null}">
<div class="alert alert-success">
<p>You have been logged out successfully.</p>
</div>
</c:if>
<div class="input-group input-sm">
<label class="input-group-addon" for="username">
<i class="fa fa-user"></i>
</label>
<input type="text" class="form-control" id="username"
name="clientusername" placeholder="Enter Username" required>
</div>
<div class="input-group input-sm">
<label class="input-group-addon" for="password">
<i class="fa fa-lock"></i>
</label>
<input type="password" class="form-control" id="password"
name="clientpassword" placeholder="Enter Password" required>
</div>
<input type="hidden" name="${_csrf.parameterName}"
value="${_csrf.token}" />
<div class="form-actions">
<input type="submit" class="btn btn-block btn-primary btn-default"
value="Log in">
</div>
</form>
</div>
Security Configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("G2BUserDetailsService")
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").permitAll()
.antMatchers("/admin/**").access("hasRole('ADMIN')")
.and().formLogin().loginPage("/login")
.usernameParameter("clientusername").passwordParameter("clientpassword")
.and().csrf()
.and().exceptionHandling().accessDeniedPage("/Access_Denied");
// .and().csrf().disable();
}
Controller:
#RequestMapping(value = "/login", method = RequestMethod.GET)
public ModelAndView loginPage() {
return new ModelAndView("login");
}
#RequestMapping(value="/logout", method = RequestMethod.GET)
public String logoutPage (HttpServletRequest request, HttpServletResponse response) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
new SecurityContextLogoutHandler().logout(request, response, auth);
}
return "redirect:/login?logout";
}
#RequestMapping(value = "/Access_Denied", method = RequestMethod.GET)
public ModelAndView accessDeniedPage(ModelMap model) {
model.addAttribute("user", getPrincipal());
return new ModelAndView("accessDenied");
}
#RequestMapping(value = "/admin", method = RequestMethod.GET)
public ModelAndView adminPage(ModelMap model) {
model.addAttribute("user", getPrincipal());
return new ModelAndView("admin");
}
private String getPrincipal(){
String userName = null;
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
userName = ((UserDetails)principal).getUsername();
} else {
userName = principal.toString();
}
return userName;
}
Almost every topic about this issue says that we need to add csrf token, but I already added. Am I missing something?
First of all csrf is enabled by default in Spring as of Spring 4.0 so there no need to explicitly enable it yourself.
Secondly, there is no endpoint for you to authenticate your login. What you're doing is sending a request to /login which only takes a GET request. You could create another controller method to receive that POST request and authenticate or you could use a UserDetailsService.
SecurityConfiguration
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login-form")
.anonymous()
.and()
.formLogin()
.loginPage("/user-login")
.defaultSuccessUrl("/admin", true) // the second parameter is for enforcing this url always
.loginProcessingUrl("/login")
.failureUrl("/user-login")
.permitAll();
}
#Autowired
private UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder pe = new BCryptPasswordEncoder();
auth.userDetailsService(userDetailsService).passwordEncoder(pe);
}
Here our view page is /user-login and the processing url is /login this means in your controller you need remove the mapping for /login and add the following:
Controller
#RequestMapping(value="/user-login", method=RequestMethod.GET)
public ModelAndView loginForm() {
return new ModelAndView("login-form");
}
And change your view.
View (login-form.jsp)
<c:url value="/login" var="loginUrl"/>
<form action="${loginUrl}" method="post" modelAttribute="user">
Username: <input type="text" id="username" name="username" placeholder=""><br>
Password: <input type="password" id="password" name="password" placeholder=""><br>
<input type="hidden"
name="${_csrf.parameterName}"
value="${_csrf.token}"/>
<button type="submit">Login</button>
</form>
I started getting the same thing when I added a successForwardUrl and found that the response on sucessful login is a POST to that endpoint or to "/" if not set. Once I enabled POST on the defined endpoint as well as GET all was fine.
You can set two endpoints for one url. But you cannot set any request parameter as required. As I saw your request map for login, you can set your request method like this:
#RequestMapping(value = "/login", method = { RequestMethod.GET, RequestMethod.POST })
public ModelAndView loginPage() {
return new ModelAndView("login");
}
Check your web.xml file you might forgot to keep "securityFilterChain"
Use this code in web.xml file
<!-- Security configuration goes here -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
You are calling for a POST yet have only defined GET methods. Change your endpoint to RequestMethod.POST
If you are using JSP/JSTL
Change
<form action="${loginUrl}" method="post"></form>
to
<form:form action="${loginUrl}" method="post" </form:form>
with tag declaration
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
solve my problem
Ensure that the Spring Security filter chain is registered.
With Java configuration, this can be done by creating a class that extends AbstractSecurityWebApplicationInitializer.
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {
}
Alternatively, edit web.xml and add the following code. (See the documentation.)
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Related
I am trying to test my web api thats secured using the standard Spring Security API. I have implemented my own User authentication service by implementing UserDetailService. However whenever I login to my application the /login api keeps returning a 302 redirect. I verified that my login page is working correctly by manually testing both good credentials and bad credentials and it did properly authenticate correctly to the homepage depending on whether the credentials were good, however it still returned a 302 for /login. Im wondering why Spring/Thymeleaf is returning a 302 redirect when performing the /login request. This is preventing my ability to test any of my guarded endpoints when locked down with spring security.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public JwtTokenFilter jwtTokenFilter() {
return new JwtTokenFilter();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.cors()
.and()
.authorizeRequests().antMatchers("/profiles/**","/img/**","/resources","/v2/**","/users", "/login", "/error/**", "/keepalive", "/register").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.defaultSuccessUrl("/")
.permitAll()
.and()
.logout();
http.addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(asList("*"));
configuration.setAllowedMethods(asList("HEAD",
"GET", "POST", "PUT", "DELETE", "PATCH"));
// setAllowCredentials(true) is important, otherwise:
// The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
configuration.setAllowCredentials(true);
// setAllowedHeaders is important! Without it, OPTIONS preflight request
// will fail with 403 Invalid CORS request
configuration.setAllowedHeaders(asList("Authorization", "Cache-Control", "Content-Type"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
Login.html Page
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
>
<head>
<title>Login</title>
<div th:replace="fragments/header :: header-css"/>
</head>
<body class="white-bg">
<div th:replace="fragments/header :: header"/>
<div class="middle-box text-center loginscreen">
<div>
<div>
<h1 class="logo-name"></h1>
</div>
<h3>Welcome to </h3>
<p>Login in. To see it in action.</p>
<form th:action="#{/login}" method="post">
<fieldset>
<div th:if="${param.error}">
<div class="alert alert-danger">
Invalid username and password.
</div>
</div>
<div th:if="${param.logout}">
<div class="alert alert-info">
You have been logged out.
</div>
</div>
<div class="form-group">
<input type="text" name="username" id="username" class="form-control"
placeholder="UserName" required="true" autofocus="true"/>
</div>
<div class="form-group">
<input type="password" name="password" id="password" class="form-control"
placeholder="Password" required="true"/>
</div>
<input type="submit" class="btn btn-lg btn-primary btn-block" value="Sign In"/>
<small>Forgot password?</small>
<p class="text-muted text-center"><small>Do not have an account?</small></p>
<a class="btn btn-sm btn-white btn-block" href="register.html">Create an account</a>
</fieldset>
</form>
<p class="m-t"> <small>DigiProof Company © 2017</small> </p>
</div>
</div>
BaseController.java for routing
#Controller
public class BaseController {
#Autowired
private UserService userService;
#GetMapping("/")
public String homeMain() {
return "home";
}
#GetMapping("/home")
public String home() {
return "home";
}
#GetMapping("/login")
public String login(Principal principal) {
if (principal!=null && ((Authentication)principal).isAuthenticated())
return "redirect:/home";
else
return "login";
}
#RequestMapping(value="/registration", method = RequestMethod.GET)
public ModelAndView registration(){
ModelAndView modelAndView = new ModelAndView();
User user = new User();
modelAndView.addObject("user", user);
modelAndView.setViewName("register");
return modelAndView;
}
#RequestMapping(value = "/registration", method = RequestMethod.POST)
public ModelAndView createNewUser(#Valid User user, BindingResult bindingResult) {
ModelAndView modelAndView = new ModelAndView();
User userByEmailExists = userService.findUserByEmail(user.getEmail());
if (userByEmailExists != null) {
bindingResult
.rejectValue("email", "error.user",
"There is already a user registered with the email provided");
}
if (bindingResult.hasErrors()) {
modelAndView.setViewName("register");
} else {
userService.save(user);
modelAndView.addObject("successMessage", "User has been registered successfully");
modelAndView.addObject("user", new User());
modelAndView.setViewName("register");
}
return modelAndView;
}
#GetMapping("/profile")
public String profile() {
return "profile";
}
#GetMapping("/activity")
public String activity() {
return "activity";
}
#GetMapping("/teams")
public String teams() {
return "teams";
}
#GetMapping("/404")
public String error404() {
return "/error/403";
}
#GetMapping("/403")
public String error403() {
return "/error/403";
}
#GetMapping("/500")
public String error500() {
return "/error/500";
}
#GetMapping("/error")
public String error() {
return "/error/500";
}
}
spring security formLogin default intercept the "/login" request, i find that your login page url is "/login" which is conflict with this filter. you can define your login page url like this:
.formLogin()
.loginPage("/page/login.html").permitAll()
and change then controller mapping from login --> /page/login
try to disable csrf
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
}
I'm developing a MVC web app with Spring. I have to use Spring security for every created page in my web app. I create a login view:
<form>
<div class="form-group">
<label for="role">Ruolo</label>
<input type="role" class="form-control" id="role" aria-describedby="role" placeholder="Inserisci ruolo">
<small id="role" class="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
</div>
<div class="form-group">
<label for="exampleInputUsername">Username</label>
<input type="username" class="form-control" id="exampleInputPassword1" placeholder="Username">
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="exampleCheck1">
<label class="form-check-label" for="exampleCheck1">Check me out</label>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
a login controller:
#RequestMapping("hi/untitled_war/login/form")
public class LoginController
{
#GetMapping
public String getLogin(Model model)
{
return "login";
}
This is my SecurityConfigClass:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public BCryptPasswordEncoder pass(){
return new BCryptPasswordEncoder();
}
#Bean
public UserDetailsService user(){
UserBuilder users = User.builder();
InMemoryUserDetailsManager manager= new InMemoryUserDetailsManager();
//1st user
manager.createUser(
users.username("ClientUser").password(new BCryptPasswordEncoder().encode("Abc12"))
.roles("Client").build() );
//2nd user
manager.createUser(
users.username("OpUser").password(new BCryptPasswordEncoder().encode("Abc123"))
.roles("Client","Operator").build() );
//3rd user
manager.createUser(
users.username("aAdmin").password(new BCryptPasswordEncoder().encode("Abc1234"))
.roles("Client", "Admin").build() );
return manager;
}
public void configure(final AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(user()).passwordEncoder(pass());
}
private static final String[] ADMIN_Matcher={
"/client/aadd/**", "/client/update/**", "client/delite/**", "cliente/view/**"
};
protected void configure(final HttpSecurity http) throws Exception{
http.authorizeRequests().antMatchers("/untitled_war/hi/**").permitAll()
.antMatchers("/login/**").permitAll()
.antMatchers("/untitled_war/hi/**")
.hasAnyRole("Anonimo","USER")
.antMatchers(ADMIN_Matcher).access("hasRole('Admin')")
.antMatchers("/client/**").hasRole("Admin")
.and()
.formLogin().loginPage("/untitled_war/hi/untitled_war/login")
.loginProcessingUrl("/login")
.failureUrl("/login/form?error")
.usernameParameter("userame").passwordParameter("password")
.and()
.exceptionHandling().accessDeniedPage("/login/form?forbidden")
.and()
.logout()
.logoutUrl("/login/form^logout")
.and()
.logout().logoutUrl("/login/form^logout");
// .and().csrf().disable()
}}
And the login doesn't work. I compile all labels into the form model, but after this my app doesn't redirect me to the page for admin role, or to a page for a simple user.
What's wrong here?
if your views in a folder, such as resources/foldername/login.html
try to return "foldername/login";
instead of return "login";
I'm trying to use Spring Security in my application, But after hitting the login processing url defined in configure method of WebSecurityConfig Class, from login page, its always redirecting to the failurUrl even if the correct username and password is provided. I have seen a lot of similar problem like this but all of the solutions that were given did not work for me.
There's the code with the security config:
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests().
antMatchers(PUBLIC_MATCHERS).
permitAll().anyRequest().authenticated();
http
.formLogin().loginPage("/index").defaultSuccessUrl("/userFront",true).failureUrl("/index?error").permitAll()
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/index?logout").deleteCookies("remember-me").permitAll()
.and()
.csrf().disable().cors().disable()
.rememberMe();
}
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userSecurityService).passwordEncoder(passwordEncoder());
}
And the service responsible for signing in (UserSecurityService.java)
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.findByUsername(username);
if (user==null) {
LOG.warn("Username {} not found",username);
throw new UsernameNotFoundException("Username "+username+"not found");
}
return user;
}
finally the login form (index.html)
<form class="form-signin" th:action="#{/index}" method="post">
<h2 class="text-center">Sign In</h2>
<div class="form-group">
<input type="text" class="form-control" placeholder="Username"
required="required" roleId="username" name="username"
id="username " autofocus="autofocus">
</div>
<div class="form-group">
<input type="password" class="form-control" placeholder="Password"
id="password" name="password" required="required"
roleId="inputPassword">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block">Sign
In</button>
</div>
<div class="clearfix">
<label class="float-left form-check-label"><input
type="checkbox" name="remember-me" id="remember-me">
Remember me</label>
</div>
</form>
What i am missing here please im tired of searching everywhere ?
Do you have a method in your controller to handle POST to /login? If not, I suggest you change your GET controller method from /index to /login, change the form to POST to /login, and update the configure() method accordingly (change "/index" to "/login").
[Update]
Make sure your login method is only serving GET and let the Spring default handle the POST:
#RequestMapping(method = { RequestMethod.GET }, value = { "/login" })
public String index() {
return "login";
}
I am trying to make a simple registration/login mechanism using Spring Boot + Thymeleaf.
I managed to register a user successfully (checked the new entry in the h2-console) but I have a problem while trying to log in.
Specifically, the application is redirected to /login?error page and there is no message at the console indicating what is wrong.
After debugging I discovered that the application does not stop at my Post controller method.
I provide you with my code:
login.html
<form th:action="#{/login}" method="post" th:object="${user}">
<div class="form-group">
<label for="username">Username</label>
<input th:field="*{username}" type="text" id="username" name="username" class="form-control" autofocus="autofocus"
placeholder="Username">
</div>
<div class="form-group">
<label for="password">Password</label>:
<input th:field="*{password}" type="password" id="password" name="password" class="form-control" placeholder="Password">
</div>
<div class="form-group">
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<input type="submit"
name="login-submit"
id="login-submit"
class="form-control btn btn-success"
value="Log In">
</div>
<div class="col-sm-6 col-sm-offset-3">
<a th:href="#{/register}">Register</a>
</div>
</div>
</div>
</form>
UserController.java
#Controller
public class UserController {
#Autowired
private UserService userService;
#Autowired
private SecurityService securityService;
#GetMapping("/login")
public String login(Model model) {
model.addAttribute("user", new User());
return "login";
}
#PostMapping("/login")
public String login(#ModelAttribute("user") User user) {
securityService.login(user.getUsername(), user.getPassword());
return "redirect:/index";
}
#GetMapping("/register")
public String register(Model model) {
model.addAttribute("user", new User());
return "register";
}
#PostMapping("/register")
public String register(#ModelAttribute("user") User user) {
userService.save(user);
return "redirect:/login";
}
}
Please help, thanks in advance!
EDIT:
SecurityConfig.java
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/h2-console/**", "/register**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
http.csrf().disable();
http.headers().frameOptions().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
#Bean("authenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
You don't have to implement the Login by yourself.
Simply create a login.html with a form like this:
<form action="#" th:action="#{/login}" method="post">
<input class="form-control" id="username" name="username" th:autofocus="true"/>
<input class="form-control" id="password" name="password"
<button>Login</button>
</form>
Spring will handle everything for you.
I don't know how to do some stuff with spring security and I hope you guys can you help me.
I have two controllers: one for login page and one for home page
#RestController
#RequestMapping("/rest/hello")
public class LoginController {
#GetMapping(value="/login")
public String getLoginPage(Model model){
return "login";
}
}
and
#RestController
#RequestMapping("/rest/hello")
public class HomeController {
#GetMapping("/home")
public String getHomePage() {
return "homePage";
}
}
For this controllers I have html pages,
login.html
<body>
<div class="limiter">
<div class="container-login100">
<div class="wrap-login100">
<div class="login100-form-title" style="background-image: url(images/bg-01.png);">
</div>
<form class="login100-form validate-form">
<div class="wrap-input100 validate-input m-b-26" data-validate="Username is required">
<span class="label-input100">Username</span>
<input class="input100" type="text" name="username" placeholder="Enter username">
<span class="focus-input100"></span>
</div>
<div class="wrap-input100 validate-input m-b-18" data-validate = "Password is required">
<span class="label-input100">Password</span>
<input class="input100" type="password" name="pass" placeholder="Enter password">
<span class="focus-input100"></span>
</div>
<div class="flex-sb-m w-full p-b-30">
<div class="contact100-form-checkbox">
<input class="input-checkbox100" id="ckb1" type="checkbox" name="remember-me">
<label class="label-checkbox100" for="ckb1">
Remember me
</label>
</div>
<div>
</div>
</div>
<div class="container-login100-form-btn">
<button class="login100-form-btn">
Login
</button>
</div>
</form>
</div>
</div>
</div>
ldif file and a configuration that looks like that:
#EnableGlobalMethodSecurity
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.formLogin();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.groupSearchBase("ou=groups")
.contextSource(contextSource())
.passwordCompare()
.passwordEncoder(new LdapShaPasswordEncoder())
.passwordAttribute("userPassword");
}
#Bean
public DefaultSpringSecurityContextSource contextSource() {
return new DefaultSpringSecurityContextSource(Arrays.asList("ldap://localhost:8389/"),
"dc=springframework,dc=org");
}
}
First of all, I want to use my login form not the default form. If I try to do this in my configuration
.formLogin().loginPage("/login");
I receive this error: This page isn’t working
localhost redirected you too many times.
Try clearing your cookies
And my second problem is that after authentication I want to redirect me to the home page but instead I receive the a message with the name of the html file: "homePage"
You might try the following.
Change the login REST controller #Get Mapping .
#RestController
#RequestMapping("/rest/hello")
public class LoginController {
#GetMapping(value="/dologin") // <---- new
public String getLoginPage(Model model){
return "login";
}
}
Match the above "/dologin" to the change in the Http configure method.
.formLogin().loginPage("/dologin");