I've generated a Spring Boot web application using Spring Initializer, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file.
Technologies used:
Spring Boot 1.4.2.RELEASE, Spring 4.3.4.RELEASE, Thymeleaf 2.1.5.RELEASE, Tomcat Embed 8.5.6, Maven 3, Java 8
This is my security config class:
#Configuration
#EnableWebSecurity
#PropertySource("classpath:/com/tdk/iot/config/app-${APP-KEY}.properties")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${securityConfig.formLogin.loginPage}")
private String loginPage;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage(loginPage)
.permitAll()
.loginProcessingUrl("/login")
.failureUrl("/login.html?error=true")
.defaultSuccessUrl("/books/list")
.and()
.exceptionHandling()
.accessDeniedPage("/denied")
.and()
.authorizeRequests()
.antMatchers("/mockup/**").permitAll()
.antMatchers("/books/**").permitAll()
.antMatchers("/welcome/**").authenticated()
.and()
.logout()
.permitAll()
.logoutSuccessUrl("/index.html");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.passwordEncoder(new StandardPasswordEncoder())
.withUser("test1").password("test1").roles("ADMIN").and()
.withUser("test2").password("test2").roles("USER").and()
.withUser("test3").password("test3").roles("SUPERADMIN");
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertyDefaultConfig() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Here the LoginController
#Controller
public class LoginController {
#RequestMapping(value={ "/", "/tdk/login"}, method = { RequestMethod.POST,RequestMethod.GET})
public String welcome(Map<String, Object> model) {
return "tdk/login";
}
}
and the template:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<div class="wrap">
<div class="login">
<div class="logo"></div>
<form th:action="#{/login.html}" method="post">
<p th:if="${loginError}" class="error">Wrong user or password</p>
<div class="input_label"><i class="fa fa-user"></i><input type="text" name="user" placeholder="User" /></div>
<div class="input_label"><i class="fa fa-key"></i><input type="password" name="pass" placeholder="Password" /></div>
<input type="submit" value="LOGIN" />
</form>
<div class="forget">
<!-- Do you forgot your password?<br/> -->
<br/>
</div>
</div>
</div>
</body>
</html>
but when I access with test1 / test1 I got this error:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sun Mar 05 20:16:11 CET 2017
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'POST' not supported
Your login page calls /login.html with HTTP POST, but your server doesn't provide such a request mapping.
The configured URL in your Spring Security configuration:
.loginProcessingUrl("/login")
is not matching the URL in your login page:
<form th:action="#{/login.html}" method="post">
See also AbstractAuthenticationFilterConfigurer#loginProcessingUrl:
Specifies the URL to validate the credentials.
try this code
.failureUrl("/tdk/login?error=true")
Controller
#Controller
public class LoginController {
#RequestMapping(value={ "/", "/tdk/login"},params = {"error"},method=RequestMethod.POST)
public String welcome(#RequestParam(value = "error", required = false) int error , ModelMap model) {
if (error == 1) {
model.addAttribute("msg", "Invalid Username or Password");
return "tdk/login";
}
else{
return "redirect:home";
}
}
}
Default method controller for #RequestMapping is GET, not POST.
You need to specify the method on the #requestMapping.
#RequestMapping(value={ "/", "/tdk/login"}, method = RequestMethod.POST)
Related
I want to implement Springboot Security for my first project and I don't know how to do the next thing:
I have the frontend and backend in different folders
on the frontend I use live-server with the url http://127.0.0.1:9000/ and the backend with the url http://localhost:8080
my login page is http://127.0.0.1:9000/login.html
Can anyone help me binding the frontend login page with the backend ?
My HTML:
<body>
<div class="login-container">
<div class="login-box">
<h1 class="login-header">LOGIN</h1>
<div class="login-input-container">
<form action="http://localhost:8080/login" method="POST">
<div class="login-input-container-email-wrapper">
<input type="text" class="login-input-email" placeholder="Email" autocorrect="off" autocomplete="off" required/>
<svg class="login-input-email-icon">
<use xlink:href="img/sprite.svg#icon-mail_outline"></use>
</svg>
</div>
<div class="login-input-container-password-wrapper">
<input type="password" class="login-input-password" placeholder="Password" required/>
<svg class="login-input-password-icon">
<use xlink:href="img/sprite.svg#icon-vpn_key"></use>
</svg>
</div>
<div class="login-submit">
<button class="login-submit-button login-submit-button--green">Submit</button>
</div>
</form>
<span class="login-separator">Or Login With</span>
<div class="login-google">
<div class="g-signin2" data-onsuccess="onSignIn" id="my-signin2"
style="display:flex;justify-content:stretch;align-items: stretch;width: 500;" data-longtitle="true"></div>
</div>
</div>
</div>
</div>
</body>
MY Springboot security config:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("http://127.0.0.1:9000/login.html").permitAll()
.defaultSuccessUrl("http://127.0.0.1:9000")
.permitAll();
}
You need to write a login controller with a rest end point where you verify credentials and you need to map that in configure() method. Not the front-end url. From front-end you should call that rest end point. Modify the configure() as below.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.defaultSuccessUrl("http://127.0.0.1:9000")
.permitAll();
}
And the login controller should be like
#RestController
public class LoginController {
#CrossOrigin
#RequestMapping(value = "/login"", method = RequestMethod.POST)
#ResponseBody
public User getUserByDetails(#RequestBody Map<String, String> credentials, HttpServletResponse response)
throws IOException {
// business logic
}
}
You can refer https://www.baeldung.com/spring-security-login-angular for further details.
Do refer below config and modify your config file as required!
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login") // Specifies the login page URL (goes to controller to look for this URL)
//.loginProcessingUrl("/login") // Specifies the URL,which is used in action on the <from> tag
//.successHandler(successHandler)
.defaultSuccessUrl("http://127.0.0.1:9000")
.and()
.logout()
.logoutUrl("/logout");
// Following is to go to direct controller URL for access denied.
// exceptionHandling().accessDeniedPage("/accessDenied");
}
Here, you must understand the difference between loginPage() and loginProcessingUrl().
Now your controller should have mapping for /login
So, your controller will have method to call your custom-login-page with below config
#GetMapping("/login")
public String customLoginPage() {
return "custom-login-page";
}
I have created one Inmemory user. Initially I tried with default formLogin. After successful login calling another api using defaultSuccessUrl till here everything fine. Now I have implemented one custom login html page when I try to login with both correct and wrong credentials getting access denied and redirecting login page again.
Anyone please help me.
Security config:
#Configuration
#EnableWebSecurity
public class SecurityConfige extends WebSecurityConfigurerAdapter {
#Autowired
private PasswordEncoder passwordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/","index","/courses","login").permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/courses", true);
}
#Override
#Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
UserDetails admin= User.builder()
.username("nithin")
.password(passwordEncoder.encode("nithin"))
.roles("ADMIN")
.authorities(new SimpleGrantedAuthority("ROLE_ADMIN"))
.build();
return new InMemoryUserDetailsManager(admin);
}
}
Html Login page:
<html lang="en">
</head>
<body>
<div class="container">
<form class="form-signin" method="post" action="/login">
<h2 class="form-signin-heading">Please login to Nithincode</h2>
<p>
<label for="username" class="sr-only">Username</label>
<input type="text" id="username" name="username" class="form-control" placeholder="Username" required=""
autofocus="">
</p>
<p>
<label for="password" class="sr-only">Password</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Password"
required="">
</p>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<button class="btn btn-lg btn-primary btn-block" type="submit">Login</button>
</form>
</div>
</body>
</html>
Login Api:
#GetMapping("login")
public String getLoginView() {
return "login";
}
I think that you are not configuring the user details service correctly and your user is not found (for which Spring shows bad credentials). Maybe try to change your configuration a bit:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService myService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/","index","/courses","login").permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/courses", true);
}
#Bean
public UserDetailsService myService() throws Exception {
UserDetails admin= User.builder()
.username("nithin")
.password(new BCryptPasswordEncoder().encode("nithin"))
.roles("ADMIN")
.authorities(new SimpleGrantedAuthority("ROLE_ADMIN"))
.build();
return new InMemoryUserDetailsManager(admin);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(myService).passwordEncoder(
new BCryptPasswordEncoder());
}
}
I set up an Authorization Service with Spring Security and Oauth2.
Everything worked fine until i tried to customize the login page.
If I login at my custom login page it redirects back to the login page and not to the callback url.
GET /login -> POST /login -> GET /login
SecurityConfig.java
#Configuration
#Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("oauth/authorize").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder
auth) throws Exception {
auth.parentAuthenticationManager(authenticationManagerBean())
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select mail,password,enabled "
+ "from users "
+ "where mail = ?")
.authoritiesByUsernameQuery("select mail,authority "
+ "from users "
+ "where mail = ?");
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
AuthorizationServerConfig.java
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private DataSource dataSource;
#Override
public void configure(final AuthorizationServerSecurityConfigurer
oauthServer) {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()");
}
#Override
public void configure(final ClientDetailsServiceConfigurer
clients) throws Exception {
clients
.jdbc(dataSource);
}
#Override
public void configure(final AuthorizationServerEndpointsConfigurer
endpoints) throws Exception {
endpoints
.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter())
.authenticationManager(authenticationManager);
}
#Bean
public TokenStore tokenStore() {
//return new JdbcTokenStore(dataSource);
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("dein-signing-key");
return converter;
}
login.html
<form action="/login" method="POST">
<div class="column">
<div class="title">Anmelden</div>
<div th:if="${param.error}" class="alert alert-error">
Invalid username and password.
</div>
<div th:if="${param.logout}" class="alert alert-success">
You have been logged out.
</div>
<input id="username" name="username" type="email" class="login input" placeholder="E-Mail Adresse"/>
<input id="password" name="password" type="password" class="login input" placeholder="Passwort"/>
<br>
<p style="text-align: center; margin-top: 20px;">Passwort vergessen?</p>
<button style=" margin-top: 20px; margin-bottom: 20px" type="submit" class="button cancel login">Anmelden</button>
</div>
</form>
You have to send the CSRF token with your custom login page, see Spring Security Reference:
9.21.1 Form Login Java Configuration
[...]
An example log in page implemented with JSPs for our current configuration can be seen below:
[...]
<c:url value="/login" var="loginUrl"/>
<form action="${loginUrl}" method="post"> 1
<c:if test="${param.error != null}"> 2
<p>
Invalid username and password.
</p>
</c:if>
<c:if test="${param.logout != null}"> 3
<p>
You have been logged out.
</p>
</c:if>
<p>
<label for="username">Username</label>
<input type="text" id="username" name="username"/> 4
</p>
<p>
<label for="password">Password</label>
<input type="password" id="password" name="password"/> 5
</p>
<input type="hidden" 6
name="${_csrf.parameterName}"
value="${_csrf.token}"/>
<button type="submit" class="btn">Log in</button>
</form>
1 - A POST to the /login URL will attempt to authenticate the user
2 - If the query parameter error exists, authentication was attempted and failed
3 - If the query parameter logout exists, the user was successfully logged out
4 - The username must be present as the HTTP parameter named username
5 - The password must be present as the HTTP parameter named password
6 - We must the section called “Include the CSRF Token” To learn more read the Section 5.1.1, “Cross Site Request Forgery (CSRF)” section of the reference
I'm making a webshop in Spring Boot 2.1.1, with ThymeLeaf 3.0.11
My login page does not appear in every cases, everytime it wants to load it gives a "TemplateInputException" and fails to show up.
I figured it out, if I delete thymeleaf variables from the body or div tags (I mean th:with attributes), then it works until it reaches the next html tag with TL variables, after that, the html page just stops to render. What could be the problem? There is no scenario where i dont use those variables, I need them in the container tag. What is the relation between the Spring Boot login page and template variables?
I copy some code, if you need any more, please let me know.
Any help would be appreciated!
Here is my Webconfig:
#Configuration
public class WebConfig implements WebMvcConfigurer{
#Override
public void addViewControllers(ViewControllerRegistry registry){
registry.addViewController("/login").setViewName("auth/login");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
My security config's configure method:
#Override
public void configure (HttpSecurity httpSec)throws Exception {
httpSec
.authorizeRequests()
.antMatchers("/", "/reg", "/login", "/css/**","/images/**",
"/js/**", "/register", "/error",
"/records", "/search", "/record", "/altercart",
"/showcart", "/category", "/viewchange",
"/images").permitAll()
.antMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/login?logout")
.permitAll();
}
My login page(using layout dialect):
<div layout:fragment="content">
<div>
<div th:if="${param.error}" th:text="#{login.error.wrongusernameorpassword}" class="col-12 error-message"/>
<div th:if="${param.logout}" th:text="#{logoutSuccess}" class="col-12 success-message"/>
<p th:text="#{logingreetings}" class="col-12"/>
<form method="post" th:action="#{login}">
<input type="text" name="username" th:placeholder="#{login.ph.username}" required class="col-12"/>
<br />
<input type="password" name="password" th:placeholder="#{login.ph.password}" required class="col-12"/>
<br />
<input type="submit" th:value="#{loginSubmitButton}" class="col-12"/>
<br /><br />
</form>
<br />
<a class="col-12 anchor" th:href="#{register}" th:text="#{misc.registration}">Registration</a>
</div>
</div>
Beggining of stack trace:
org.thymeleaf.exceptions.TemplateInputException: An error happened during
template parsing (template: "class path resource
[templates/auth/login.html]"
I am having errors login from the HTML using Spring Security, Spring MVC and JPA.
This is my login.HTML
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en" xmlns:th="http://www.thymeleaf.org">>
<title>Spring Framework Guru</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body class="security-app">
<div class="details">
<h2>Spring Security - App</h2>
</div>
<form action="/login" method="post">
<div class="lc-block">
<div>
<input type="text" class="style-4" name="username"
placeholder="User Name" />
</div>
<div>
<input type="password" class="style-4" name="password"
placeholder="Password" />
</div>
<div>
<input type="submit" value="Sign In" class="button red small" />
</div>
<th:if test="${param.error ne null}">
<div class="alert-danger">Invalid username and password.</div>
</th:if>
<th:if test="${param.logout ne null}">
<div class="alert-normal">You have been logged out.</div>
</th:if>
</div>
<input type="hidden" name="${_csrf.parameterName}"
value="${_csrf.token}" />
</form>
</body>
</html>
This is WebSecurity class:
#Configuration
#EnableWebSecurity
#ComponentScan(basePackageClasses = CustomUserDetailsService.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordencoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/hello").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll()
.and()
.formLogin().loginPage("/login")
.usernameParameter("username").passwordParameter("password")
.and()
.logout().logoutSuccessUrl("/login?logout")
.and()
.exceptionHandling().accessDeniedPage("/403")
.and()
.csrf();
}
#Bean(name = "passwordEncoder")
public PasswordEncoder passwordencoder() {
return new BCryptPasswordEncoder();
}
}
UserDetails service class:
#Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
private final UserRolesRepository userRolesRepository;
#Autowired
public CustomUserDetailsService(UserRepository userRepository, UserRolesRepository userRolesRepository) {
this.userRepository = userRepository;
this.userRolesRepository = userRolesRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUserName(username);
if (null == user) {
throw new UsernameNotFoundException("No user present with username: " + username);
} else {
List<String> userRoles = userRolesRepository.findRoleByUserName(username);
return new CustomUserDetails(user, userRoles);
}
}
}
I always have 405 error:
2016-10-16 12:15:30.710 WARN 2932 --- [nio-8080-exec-3] o.s.web.servlet.PageNotFound : Request method 'POST' not supported
Any ideas why is not calling the "configure(HttpSecurity http)". Am I missing something?
Thank you very much
Andres
Try adding
.formLogin().loginPage("/xxx").permitAll()
.defaultSuccessUrl("/xxx")
.failureUrl("/xxx?error")
In addition
One typical reason in Spring MVC Applications for controller methods and pages not found is Spring's weird mapping convention to build URLs by adding a new link (or the form action="x") to the end of current URL. 'Request Method POST Not Supported' only means that your request is pointed to somewhere where nothing is accepting the POST request => URL mapping fails. On JSP sites you should always specify URLs like
<form action="${pageContext.request.contextPath}/login" method="post">
or with JSTL
<c:url value="/login" var="loginUrl" />
<form action="${loginUrl}" method="post">
Then you can be sure that your link is set right after the application root in URL. This will save you from a lot of unnecessary problems in the future.