I want to render a navigation view from a controller using Thymeleaf. However, the navigation should be based on user's authorities so I do not want to exclude it from Spring Security. I therefore use a UrlTemplateResolver:
package it.vertyze.platform.web.controllers;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.templateresolver.UrlTemplateResolver;
#Configuration
public class VZMVCConfig extends WebMvcConfigurerAdapter {
#Autowired
private SpringTemplateEngine templateEngine;
#PostConstruct
public void templateResolverExtension(){
UrlTemplateResolver urlTemplateResolver = new UrlTemplateResolver();
urlTemplateResolver.setOrder(20);
templateEngine.addTemplateResolver(urlTemplateResolver);
}
}
However, when I log into my site, the link does not even get resolved. Instead, I am being redirected to the login page. When I disable the security, I get a template not found error, so the template resolver does not render the controller's output, but when I navigate to it, it renders precisely the HTML I want.
Here is my security config:
package it.vertyze.platform.web.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
#Configuration
#EnableWebMvcSecurity
public class VZWebSecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
VZUserDetailsService userDetailsService;
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(userDetailsService).passwordEncoder(encoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Bean
public PasswordEncoder encoder(){
return new BCryptPasswordEncoder();
}
}
And here is where I call the link in the enclosing template:
<div th:replace="${topNavLinks.getHref()}"></div>
Related
package com.codewitheshan.blog.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.codewitheshan.blog.security.CustomUserDetailService;
import com.codewitheshan.blog.security.JwtAuthenticationEntryPoint;
import com.codewitheshan.blog.security.JwtAuthenticationFilter;
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Autowired
private CustomUserDetailService customUserDetailService;
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeHttpRequests()
.antMatchers("/api/v1/auth/login").permitAll()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(this.jwtAuthenticationEntryPoint)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(this.jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(this.customUserDetailService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public AuthenticationManager authenticationManagerBean(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
}
There is error showing in antMatchers
**I'm getting an error for "antMatchers". It is not recognised. I have tied searching but did not get any thing related to this.
Error is:
The method antMatchers(String) is undefined for the type AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry
How to fix it? **
This method has changed to :
.requestMatchers({your-matcher})
So, in your case :
http
.csrf()
.disable()
.authorizeHttpRequests()
.requestMatchers("/api/v1/auth/login").permitAll()
.anyRequest()
.authenticated()
// ...
More information : https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html
I hope this has helped you
Below is my PasswordEncoder Class
package com.example.springsecuritybasic.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
#Configuration
public class PasswordConfig {
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Below is my ApplicationSecurityConfig Class
package com.example.springsecuritybasic.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
#Configuration
#EnableWebSecurity
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter{
private final PasswordEncoder passwordEncoder;
#Autowired
public ApplicationSecurityConfig(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/","index","/css/*","/js/*")
.permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
#Override
protected UserDetailsService userDetailsService() {
UserDetails annasmithUser = User.builder()
.username("anna")
.password(passwordEncoder.encode("password"))
.roles("STUDENT")
.build();
return new InMemoryUserDetailsManager(
annasmithUser
);
}
}
Below is my Main Class -
package com.example.springsecuritybasic;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SpringsecuritybasicApplication {
public static void main(String[] args) {
SpringApplication.run(SpringsecuritybasicApplication.class, args);
}
}
From the WebSecurityConfigurerAdapter#userDetailsService Javadoc:
Allows modifying and accessing the UserDetailsService from userDetailsServiceBean() without interacting with the ApplicationContext. Developers should override this method when changing the instance of userDetailsServiceBean().
To configure a custom user, you can register a UserDetailsService bean rather than overriding the method
#Bean
protected UserDetailsService userDetailsService() {
UserDetails annasmithUser = User.builder()
.username("anna")
.password(this.passwordEncoder.encode("password"))
.roles("STUDENT")
.build();
return new InMemoryUserDetailsManager(
annasmithUser
);
}
When I run the app my login page is without images, but when I log in and log out, the login page is styled as it should be.
Can problem be the Security file or something else?
All answers i found are related to the problem where spring won't load CSS at all (.antMatchers(" resources/", "/static/", "/css/", "/js/", "/images/**")) but I don't think this is the same. I couldn't find solution for this.
This is my security file:
package com.example.dnevnikjartest.configuration;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
private AuthenticationSuccessHandler authenticationSuccessHandler;
#Autowired
public SecurityConfiguration(AuthenticationSuccessHandler authenticationSuccessHandler) {
this.authenticationSuccessHandler = authenticationSuccessHandler;
}
#Autowired
private DataSource dataSource;
#Value("${spring.queries.users-query}")
private String korisniciQuery;
#Value("${spring.queries.roles-query}")
private String ulogeQuery;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().usersByUsernameQuery(korisniciQuery).authoritiesByUsernameQuery(ulogeQuery)
.passwordEncoder(bCryptPasswordEncoder).dataSource(dataSource);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/resource/**").permitAll()
.antMatchers("/roditelj/**").hasAuthority("roditelj")
.antMatchers("/admin/**").hasAuthority("admin")
.antMatchers("/ucitelj/**").hasAuthority("ucitelj")
.antMatchers("/direktor/**").hasAuthority("direktor")
.anyRequest()
.authenticated().and().csrf().disable().formLogin().loginPage("/login").failureUrl("/login?error=true")
.successHandler(authenticationSuccessHandler)
.usernameParameter("username")
.passwordParameter("password").and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/").and()
.exceptionHandling().accessDeniedPage("/access-denied");
}
}
}
I found a solution to my own question.
I insert this code in the class I mentioned in the question and I created folder images in resource -> static. Previously I have all files images files and .css mixed directly in static without folders.
String[] staticResources = {
"/css/**",
"/images/**",
"/fonts/**",
"/scripts/**",};
This is how whole class looks now
package com.example.dnevnikjartest.configuration;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
private AuthenticationSuccessHandler authenticationSuccessHandler;
#Autowired
public SecurityConfiguration(AuthenticationSuccessHandler authenticationSuccessHandler) {
this.authenticationSuccessHandler = authenticationSuccessHandler;
}
#Autowired
private DataSource dataSource;
#Value("${spring.queries.users-query}")
private String korisniciQuery;
#Value("${spring.queries.roles-query}")
private String ulogeQuery;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().usersByUsernameQuery(korisniciQuery).authoritiesByUsernameQuery(ulogeQuery)
.passwordEncoder(bCryptPasswordEncoder).dataSource(dataSource);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
String[] staticResources = {
"/css/**",
"/images/**",
"/fonts/**",
"/scripts/**",};
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/resource/**").permitAll()
.antMatchers("/roditelj/**").hasAuthority("roditelj")
.antMatchers("/admin/**").hasAuthority("admin")
.antMatchers("/ucitelj/**").hasAuthority("ucitelj")
.antMatchers(staticResources).permitAll()
.anyRequest()
.authenticated().and().formLogin().loginPage("/login").failureUrl("/login?error=true")
.successHandler(authenticationSuccessHandler)
.usernameParameter("username")
.passwordParameter("password").and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/").and()
.exceptionHandling().accessDeniedPage("/access-denied");
}
}
Here is the sample controller. My problem is basically when i am entering the base url it is redirecting to inner page not in the log in page. What i want. What should i do to achieve this.
Here is the sample controller. My problem is basically when i am entering the base url it is redirecting to inner page not in the log in page. What i want. What should i do to achieve this.
package com.sushovan.security.controller;
import javax.validation.groups.ConvertGroup;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class HomeController {
#RequestMapping("/")
public String home() {
return "home.jsp";
}
#RequestMapping("/login")
public String loginPage() {
return "login.jsp";
}
#RequestMapping("/logout-success")
public String logoutPage() {
return "logout.jsp";
}
}
Here is the sample Security Configuration class.Mostly all configuration have been done here.
package com.sushovan.security.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.AntPathMatcher;
#Configuration
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
/**This is for authentication from database**/
#Bean
public AuthenticationProvider authProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
//provider.setPasswordEncoder(NoOpPasswordEncoder.getInstance());//This is for not use any encryption
provider.setPasswordEncoder(new BCryptPasswordEncoder());//This is for BCryptPasswordEncoder
return provider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/login")
.permitAll()
.and()
.formLogin()
.loginPage("/login")
.usernameParameter("userName").passwordParameter("password")
.permitAll()
.and()
.logout().invalidateHttpSession(true)
.clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/logout-success").permitAll();
}
}
Spring security filters algorithm works like this ;
is web resource protected ?
is user authenticated ?
is user authorized ?
So if its not authenticated it redirect request to login page, which is what you want.
So you should update your configure method
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.usernameParameter("userName").passwordParameter("password")
.permitAll()
.and()
.logout().invalidateHttpSession(true)
.clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/logout-success").permitAll();
}
can you please try this and let me know if it works ?
I have set the root path as:-
server.contextPath=/myspringBootApp (in Application.propertes) file.
and changed the configuration file as:-
package com.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
public CustomAuthenticationEntryPoint unauthorizedHandler;
#Autowired
MyDaoAuthenticationProvider authProvider;
#Bean
public CustomAuthenticationTokenFilter authenticationTokenFilterBean() {
return new CustomAuthenticationTokenFilter();
}
#Autowired
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider.authProvider());
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.authorizeRequests()
// UI related urls
.antMatchers(
HttpMethod.GET,
"/",
"/myspringBootApp/login",
"/content/**",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/assets/**"
).permitAll()
//Back end - auth layer
.antMatchers("/auth/user").permitAll()
//Back end - actual rest layer
.antMatchers(HttpMethod.POST,"/auth/login").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler);
httpSecurity.addFilterBefore(authenticationTokenFilterBean(),UsernamePasswordAuthenticationFilter.class)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
The above code is not working and loading the UI. I tried changing the UI URLs to /myspringBootApp/favicon.ico, but this also dint give desired result.
Can anyone help me to find a solution?
I think you can use the WebSecurity part of the WebSecurityConfigurerAdapter for this:
#Override
public void configure(final WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/")
.antMatchers("/favicon.ico")
.antMatchers("/**.css")
.antMatchers("/webjars/**")
...