I'm working on MVC web app. Using Spring Boot 2.0.1 with Spring Security.
And I get error 404 when try reaching static resources.
I've tried diefferent things, I've read many topics, but can't find any solution.
Configuretion class:
#SpringBootApplication
#EnableWebMvc
public class FriendlyFireChessApplication extends SpringBootServletInitializer implements WebMvcConfigurer {
#Autowired
private SpringApplicationContext springApplicationContext;
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(FriendlyFireChessApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(FriendlyFireChessApplication.class, args);
}
/*
some beans here
*/
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
}
Project strucutre:
index.html:
<DOCTYPE html>
<html lang="en">
<head>
<title>Friendly fire chess</title>
<link rel="stylesheet" type="text/css" href='/static/css/style.css'/>
</head>
<body>
<header>
<div class="main_header">
<div>
<a id="icon"><img src='/static/img/logo_1.png' width="40" height="70" border="0" /></a>
</div>
<div id="main_title">
<span>Friendly Fire Chess</span>
</div>
<div class="authentication_bar">
<div>
<span><a id="log_in_button" href='http://www.ffchess.org/login'>Login</a></span>
</div>
<div>
<span><a id="sign_in_button" href="http://www.ffchess.org/signin">Sign In</a></span>
</div>
</div>
</div>
</header>
</html>
Security settings:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL)
.permitAll()
.antMatchers(HttpMethod.GET, SecurityConstants.VERIFICATION_EMAIL_URL)
.permitAll()
.antMatchers(HttpMethod.POST, SecurityConstants.PASSWORD_RESET_REQUEST_URL)
.permitAll()
.antMatchers(HttpMethod.POST, SecurityConstants.PASSWORD_RESET_URL)
.permitAll()
.antMatchers(SecurityConstants.H2_CONSOLE)
.permitAll()
.antMatchers(SecurityConstants.HOME_PAGE)
.permitAll()
.antMatchers("/resources/**", "/static/css/**", "/static/img/**")
.permitAll()
.anyRequest().authenticated().and()
.addFilter(getAuthenticationFilter())
.addFilter(new AuthorizationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
What's wrong with all of this?
As per project structure, all your resources will be copied to static directory under your classpath and there won't be any location like resources. Hence, it would not be able to resolve.
resource location should be specified along with classpath
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
for safer side, you can class multiple location lookup as well like this
.addResourceLocations(new String[]{"classpath:/static/", "/"});
Spring includes these by default unless overridden
["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/", "/"]
Related
My SpringWebConfiguration.class is here:
#Configuration
#EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
private static Logger logger = LoggerFactory.getLogger(SpringSecurityConfig.class);
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
logger.info("-----configure(HttpSecurity http)");
http.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers("/admin/**").hasAnyRole("ADMIN")
.antMatchers("/user/**").hasAnyRole("USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/login")
.loginPage("/login")//
.defaultSuccessUrl("/userAccountInfo")//
.failureUrl("/login?error=true")//
.usernameParameter("username")//
.passwordParameter("password")
.defaultSuccessUrl("/")
.permitAll().
and().rememberMe().rememberMeParameter("remember-me").key("uniqueAndSecret").tokenValiditySeconds(1296000).userDetailsService(userDetailsService)
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/")
.deleteCookies("guid")
.deleteCookies("JSESSIONID")
.permitAll()
.and().csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
logger.info("-----configureGlobal(AuthenticationManagerBuilder auth)");
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
}
My LoginController:
#RestController
public class LoginController() {
#GetMapping("/login")
public String login(Model model) {
return "/login";
}
}
My html file:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Bootstrap Example</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<h2>Stacked form</h2>
<form th:action="#{/login}" method="post">
<div class="form-group">
<label for="email">Email:</label>
<input type="email" class="form-control" id="email" placeholder="Enter email" name="email">
</div>
<div class="form-group">
<label for="pwd">Password:</label>
<input type="password" class="form-control" id="pwd" placeholder="Enter password" name="pswd">
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" type="checkbox" name="remember"> Remember me
</label>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</body>
</html>
When I open browser and go to "localhost:8080/login". It returns String "/login", not html login page. Why? Maybe I missed something to connect to my html file. I think my controller need something like an url to connect to html file. I don't understand how it work correctly. Help me please!
First, you need to modify LoginController
#Controller
public class LoginController() {
#GetMapping("/login")
public String login(Model model) {
return "/login";
}
}
If it does not work then convert html to jsp page.
#Configuration
#EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
return resolver;
}
}
Add ViewResolver to your configuration class along with your code.
You need to add a view resolver to your spring configurations.
This ViewResolver allows us to set properties such as prefix or suffix to the view name to generate the final view page URL
Example:
#EnableWebMvc
#Configuration
#ComponentScan("com.baeldung.web")
public class WebConfig implements WebMvcConfigurer {
#Bean
public ViewResolver internalResourceViewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setViewClass(JstlView.class);
bean.setPrefix("/WEB-INF/view/");
bean.setSuffix(".jsp");
return bean;
}
}
For such simplicity of the example, we don't need a controller to process the request.
We only need a simple jsp page, placed in the /WEB-INF/view folder as defined in the configuration.
Source:
https://www.baeldung.com/spring-mvc-view-resolver-tutorial
I'm learning Spring Security at creating simple login form. I'm using java configuration. I've in-memory users and a simple filter chain.
But when I input an existing username and password combination Spring redirect me back to login form with url: login?error.
This is my Spring Security configuration class:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// #Autowired
// AuthProvider provider;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user1").password("").roles("USER")
.and()
.withUser("user2").password("").roles("USER")
.and()
.withUser("admin").password("1").roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
}
// #Override
// protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.authenticationProvider(provider);
// }
}
This is my JSP form:
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
${message}
<br>
<form method="post" action="/login">
<input type="text" name="login"/>
<input type="text" name="pass"/>
<input type="submit" value="enter"/>
</form>
</body>
</html>
in your code
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user1").password("").roles("USER")
.and()
.withUser("user2").password("").roles("USER")
.and()
.withUser("admin").password("1").roles("ADMIN");
}
replace the #Autowired with #Override
and follow this practice here [1]: https://www.baeldung.com/spring-security-login
... auth.inMemoryAuthentication()
.withUser("user1").password(passwordEncoder().encode("user1Pass")).roles("USER")
.and() ...
using BCryptPasswordEncoder as follows as a bean in same code
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Take a look at formLogin(String loginPage) javadoc - default parameters for username and password are username and password. So you should reference them like that in your .jsp and then the login should work. So you should try refactoring jsp to following:
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
${message}
<br>
<form method="post" action="/login">
<input type="text" id="username" name="username"/>
<input type="password" id="password" name="password"/>
<input type="submit" value="enter"/>
</form>
</body>
</html>
For admin user, try with:
.withUser("admin").password("{noop}1").roles("ADMIN");
This is a way to store passwords in a plain text (obviously a not recommended way...). By adding {noop} prefix, you indicate you want to use NoopPasswordEncoder.
Otherwise, you should specify password encoder, for example:
#Bean
public BCryptPasswordEncoder passEncoder() {
return new BCryptPasswordEncoder();
}
and update your SecurityConfig like:
#Autowired
private BCryptPasswordEncoder passEncoder;
#Override
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(passEncoder)
.withUser("user1").password("").roles("USER")
.and()
.withUser("user2").password("").roles("USER")
.and()
.withUser("admin").password("1").roles("ADMIN");
}
I had all three error in my code, described above. I acceped all three solutions and it works for me. Thank you!
Worked code:
My SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user1").password(passwordEncoder().encode("1")).roles("USER")
.and()
.withUser("user2").password(passwordEncoder().encode("1")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder().encode("1")).roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.usernameParameter("login")
.passwordParameter("pass")
.permitAll();
}
}
My jsp form:
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
${message}
<br>
<form method="post" action="/login">
<input type="text" name="login"/>
<input type="text" name="pass"/>
<input type="submit" value="enter"/>
</form>
</body>
</html>
I encountered a problem when integrating spring security with spring boot.After Overwriting the WebSecurityConfigurerAdapter, I can't redirect to the successful page(wlecome.ftl) when I've accessed the login page.It's aways redirect to the login page(index.ftl) without error logs.
Is there anyting I missed?Help is aways appreciated,thanks!
SecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
MyUserDetailsService detailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/user/**").hasRole("USER")
.anyRequest().fullyAuthenticated()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=true")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/user/welcome")//add this but not work
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.permitAll();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/js/**", "/css/**", "/images/**", "/**/favicon.ico");
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("USER");
}
Login.Controller
#Controller
public class LoginController {
#RequestMapping("/login")
public String login(){
return "index";
}
#RequestMapping("/user/welcome")
public String welcome(){
return "user/welcome";
}
index.ftl(the login page)
<!DOCTYPE html>
<#import "/spring.ftl" as spring />
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="<#spring.url '/login' />" method="post">
username:<input type="text" name="username"><br>
password:<input type="password" name="password"><br>
<input type="submit" value="login">
</form>
</body>
</html>
welcome.ftl
<html>
<head>
</head>
<body>
welcome
</body>
</html>
You didn't set the successful login url and you anywhere redirect after login success. Put the:
defaultSuccessUrl("/user/welcome")
after the:
passwordParameter("...")
in your security configuration.
try this
.defaultSuccessUrl("/user/welcome",true)
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
}
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.