Wrong redirection in Spring MVC app - spring

Im going to be quick. I have a Spring MVC project, and Im using Spring Security, so after I successfully log in the server redirects me to the application context instead of the index page. Any idea why this is happening, I suspect it may be a security problem, but so far I haven´t figure it out, so please I need help on this one.
My login form action is this: ${loginUrl}
And the redirection problem only happens the first time i try to log in, if I log out and log in again the server redirects me correctly.
Here is my code:
Web Security Config class:
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
ServicioInicioSesion inicioSesion;
#Autowired
MessageSource messageSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/addUsuarios").permitAll()
.antMatchers("/roles/**", "/usuarios/**").hasAuthority("Administrador")
.antMatchers("/editarPerfil").authenticated()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").defaultSuccessUrl("/index")
.permitAll()
.and()
.logout()
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutSuccessUrl("/login")
.and()
.exceptionHandling().accessDeniedPage("/403");
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/static/**");
}
#Bean(name = "authenticationManager")
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(inicioSesion);
auth.setMessageSource(messageSource);
auth.setPasswordEncoder(passwordEncoder());
return auth;
}
#Bean
public PasswordEncoder p`enter code here`asswordEncoder() {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
return passwordEncoder;
}
}
Index Controller class
#Controller
public class IndexController {
#RequestMapping(value = "/index", method = RequestMethod.GET)
public String showIndex() {
return "index";
}
}

Alberto. Try this one:
1 - replace value = "/index" by value = {"/","/index"}
2 - remove method parameter
#RequestMapping(value = {"/","/index"})
When you submit authentication form in the request you have POST data, but in your case you have RequestMethod.GET

Related

Spring Security not letting unauthorized users reach the login page

I am using Spring Security and trying to add a custom login form, the browser does get redirected to the correct URL but I get a message along the lines of
The page isn’t redirecting properly
and can't see the login page at all.
Under the network tab (when I press F12) I see multiple requests to my custom login page, so I'm guessing Spring sees I'm unauthorized then redirects me to the login page over and over effectively creating a loop.
This is the code for my security configuration:
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/showMyLoginPage")
.permitAll();
return http.build();
}
Tried removing the loginPage() bit, solving the issue but yielding the default login page.
requested image
Request and response show no info..
Controller I'm using
#Controller
public class MainController {
#RequestMapping("/")
public String testMapping()
{
return "home";
}
#RequestMapping("/showMyLoginPage")
public String loginPage()
{
return "users-login";
}
}
My debug log: https://pastebin.com/LagTN71L
My configuration classes: (won't show hibernate or c3p0)
#Configuration
#EnableWebMvc
#ComponentScan("com.user.springsecuritydemo")
public class MainConfig implements WebMvcConfigurer {
#Bean
InternalResourceViewResolver configuInternalResourceViewResolver()
{
return new InternalResourceViewResolver("/WEB-INF/view/", ".jsp");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
}
}
public class SpringMVCDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses()
{
// TODO Auto-generated method stub
return null;
}
#Override
protected Class<?>[] getServletConfigClasses()
{
return new Class[] { MainConfig.class };
}
#Override
protected String[] getServletMappings()
{
return new String[] { "/" };
}
}
Okay, I ended up fixing it, so basically my jsp page is under my /WEBINF/ directory, which was not allowed for everyone to access therefore the browser couldn't get to the login page...
This is my SecurityFilterChain method:
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception
{
http
.authorizeHttpRequests()
.requestMatchers("/WEB-INF/**")
.permitAll();
http.authorizeHttpRequests()
.anyRequest()
.authenticated();
http
.formLogin()
.loginPage("/login")
.permitAll();
return http.build();
}

Request method 'POST' is not supported

I'm trying to upgrade Spring Boot from 2.7.6 to 3.0.1. I have a problem during the login action. The following is my new WebSecurityConfig:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
private final CustomUserDetailsService customUserDetailsService;
private final CustomizeAuthenticationSuccessHandler customizeAuthenticationSuccessHandler;
public WebSecurityConfig(CustomUserDetailsService customUserDetailsService, CustomizeAuthenticationSuccessHandler customizeAuthenticationSuccessHandler) {
this.customUserDetailsService = customUserDetailsService;
this.customizeAuthenticationSuccessHandler = customizeAuthenticationSuccessHandler;
}
#Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(customUserDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public AccessDeniedHandler accessDeniedHandler(){
return new CustomAccessDeniedHandler();
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.requestMatchers("/").permitAll()
.requestMatchers("/login").permitAll()
.authenticated()
.and()
.csrf().disable()
.formLogin()
.successHandler(customizeAuthenticationSuccessHandler)
.loginPage("/login")
.failureUrl("/login?error=true")
.usernameParameter("email")
.passwordParameter("password")
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.invalidateHttpSession(true)
.logoutSuccessUrl("/login?logout=true")
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.and()
.authenticationProvider(authenticationProvider());
http
.sessionManagement()
.maximumSessions(1)
.expiredUrl("/login?expired=true");
return http.build();
}
// This second filter chain will secure the static resources without reading the SecurityContext from the session.
#Bean
#Order(0)
SecurityFilterChain resources(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**").permitAll()
.anyRequest().permitAll())
.requestCache().disable()
.securityContext().disable()
.sessionManagement().disable();
return http.build();
}
}
Follow my CustomUserDetailService:
#Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User findUserByEmail(String email) {
System.out.println(email);
User user = userRepository.findByEmail(email.toLowerCase());
System.out.println(user.getEmail());
return userRepository.findByEmail(email.toLowerCase());
}
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email.toLowerCase());
if (user != null) {
List<GrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority( user.getRole()));;
return buildUserForAuthentication(user, authorities);
} else {
throw new UsernameNotFoundException("username not found");
}
}
private UserDetails buildUserForAuthentication(User user, List<GrantedAuthority> authorities) {
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), authorities);
}
}
When I run the application I see the login page, but when I enter the credential and press submit I receive the error:
Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' is not supported]
and Tomcat shows:
HTTP Status 405 – Method Not Allowed Type Status Report
Message Method 'POST' is not supported.
I searched for a solution but really I don't understand where is the problem.
To use multiple HttpSecurity instances, you must specify a security matcher, otherwise the first SecurityFilterChain will process all requests, and no requests will reach the second chain.
See this section of the Spring Security reference documentation.
In your case the SecurityFilterChain called resources is matching all requests, because you don't have a security matcher.
Since the resources chain does not configure formLogin then Spring Security does not create the default /login POST endpoint.
You can fix this by changing requests to:
#Bean
#Order(0)
SecurityFilterChain resources(HttpSecurity http) throws Exception {
http
.securityMatchers((matchers) -> matchers
.requestMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**") // the requests that this SecurityFilterChain will process
)
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().permitAll())
.requestCache().disable()
.securityContext().disable()
.sessionManagement().disable();
return http.build();
}
If you want more details on the difference between authorizeHttpRequests and requestMatchers you can check out this question.
This error typically occurs when the method in the controller is not mapped to a post request. Should be something like:
#RequestMapping(value = "/login", method = {RequestMethod.GET, RequestMethod.POST})
public ModelAndView login(...

Spring Boot autologin after registration not working

I have a login and a registration page. I wanted to achieve the feature of autologin after the registration. I have gone through various docs and finally came up with this. Can someone figure out what went wrong here?
Web Security Configuration
#Configuration
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder()
{
return new BCryptPasswordEncoder();
}
#Autowired
public UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/register/**","/css/**","/js/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/welcome",true)
.permitAll()
.and()
.rememberMe()
.rememberMeParameter("rememberme")
.rememberMeCookieName("myLogin")
.tokenValiditySeconds(360*60*60)
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.invalidateHttpSession(true)
.clearAuthentication(true)
.deleteCookies("myLogin");
}
}
Controller
#Autowired
protected AuthenticationManager authenticationManager;
#Autowired
UserRepo repo;
#Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
#RequestMapping("/login")
public String loginpage()
{
return "index";
}
#RequestMapping("/welcome")
public String welcomePage()
{
return "welcome";
}
#RequestMapping(value = "/register", method = RequestMethod.GET)
public String register(Model model)
{
model.addAttribute("user", new User());
return "register";
}
#RequestMapping(value = "/register",method = RequestMethod.POST)
public String registerIt(#Valid #ModelAttribute("user")User user, BindingResult result, Model model, HttpServletRequest request)
{
if(result.hasErrors())
{
return "register";
}
Roles roles1=new Roles();
Roles roles2=new Roles();
roles1.setRoles("ADMIN");
roles2.setRoles("USER");
ArrayList<Roles> roleList=new ArrayList<>();
roleList.add(roles1);
roleList.add(roles2);
user.setRoles(roleList);
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
repo.save(user);
UsernamePasswordAuthenticationToken token=new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword());
request.getSession();
token.setDetails(new WebAuthenticationDetails(request));
Authentication auth=authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(auth);
return "welcome";
}
Still, after the registration, the page redirects to the Login page itself. I am not able to figure out what went wrong.... Please help...
Try this to init the Auth:
Ref: org.springframework.security.web.authentication.AuthenticationFilter#successfulAuthentication
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
org.springframework.security.core.userdetails.UserDetails userDetails =
new YOURUserDetail( PARAMS );
//create instance of your AUTH object
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, other params )
securityContext.setAuthentication(authentication);
SecurityContextHolder.setContext(securityContext);

Auth websocket session after manual web auth

I am using Spring Security with STOMP WebSocket on SpringBoot. Auth on websocket worked fine with this config when I used simple login form:
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/webjars/**", "/resources/**").permitAll()
.antMatchers("/register").anonymous()
.anyRequest()
.fullyAuthenticated()
.and()
.formLogin()
.loginPage("/login")
.successHandler(customLoginSuccessHandler)
.failureUrl("/login?error")
.permitAll()
.and()
.csrf().disable()
.logout().logoutSuccessHandler(customLogoutSuccessHandler);
}
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.nullDestMatcher().authenticated()
.simpTypeMatchers(CONNECT).authenticated()
.simpSubscribeDestMatchers(Channel.SYSTEM_ERROR.value()).permitAll()
.simpDestMatchers("/app/publish*").hasRole("USER")
.simpSubscribeDestMatchers("/user/**", "/topic/**", "/system/*").hasRole("USER")
.anyMessage().denyAll();
}
But when I wanted to manually auth client after register new user in RegisterController:
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String signup(#Valid #ModelAttribute SignupForm signupForm, Errors errors) {
if (errors.hasErrors()) {
return SIGNUP_VIEW_NAME;
}
User user = signupForm.createAccount();
try {
userService.persist(user);
} catch (EntityExistsException ex) {
errors.rejectValue("login", "user.exists");
return SIGNUP_VIEW_NAME;
}
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(user, null, Collections.singletonList(new SimpleGrantedAuthority("USER"))));
return "redirect:/";
}
I've got problem with auth websocket. When I get redirected to page where websocket connects I am getting org.springframework.security.access.AccessDeniedException: Access is denied
So. Problem was in define Role. In controller when I defined new SimpleGrantedAuthority("USER") it should be "ROLE_USER" because Spring adds refix ROLLE_ by default. Sure we can change default behaviour of this by add next in WebSecurity configuration
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**", "/favicon.ico");
web.expressionHandler(new DefaultWebSecurityExpressionHandler() {
#Override
protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) {
WebSecurityExpressionRoot root = (WebSecurityExpressionRoot) super.createSecurityExpressionRoot(authentication, fi);
root.setDefaultRolePrefix(""); //remove the prefix ROLE_
return root;
}
});
}
. Yes, dummy mistake but so common. So I will leave it here

How to configure custom authentication filter in spring security - using java config

I'm trying to configure a custom filter for spring security based authentication. It's a simple override of the usernamepasswordfilter. My problem is I don't know how to configure it using java configuration. Every time I hit "/admin/login" - it's entering my filter and causing an exception rather than going to the login page - but the antmatchers should be allowing access to /admin/login.
If I disable my filter, it works fine. I've read a few of the related questions but none seem to lead me to an answer.
Can anyone advise how to fix my configuration below to support the custom filter?
/**
* the security configuration.
*/
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
UserNotifier userNotifier() {
UserNotifier us = new UserNotifier();
return us;
}
#Bean
AuthenticationProvider customAuthenticationProvider() {
SystemUserAuthenticationProvider impl = new SystemUserAuthenticationProvider();
/* other properties etc */
return impl ;
}
#Bean
SystemUserService systemUserService(){
SystemUserService systemUserService = new SystemUserService();
return systemUserService;
}
#Bean
SystemAuthenticationFilter systemAuthenticationFilter() throws Exception {
SystemAuthenticationFilter f = new SystemAuthenticationFilter();
f.setAuthenticationManager(this.authenticationManager());
f.setPasswordParameter("password");
f.setUsernameParameter("email");
f.setPostOnly(true);
f.setAuthenticationFailureHandler(exceptionMappingAuthenticationFailureHandler());
f.setAuthenticationSuccessHandler(savedRequestAwareAuthenticationSuccessHandler());
f.setFilterProcessesUrl("/login");
return f;
}
#Bean
SavedRequestAwareAuthenticationSuccessHandler savedRequestAwareAuthenticationSuccessHandler(){
SavedRequestAwareAuthenticationSuccessHandler sv = new SavedRequestAwareAuthenticationSuccessHandler();
sv.setDefaultTargetUrl("/admin/customers");
return sv;
}
#Bean
AuditorAware<SystemUser> auditorAware(){
SystemUserAuditorAware adw = new SystemUserAuditorAware();
return adw;
}
#Bean
ExceptionMappingAuthenticationFailureHandler exceptionMappingAuthenticationFailureHandler(){
ExceptionMappingAuthenticationFailureHandler ex = new ExceptionMappingAuthenticationFailureHandler();
Map<String, String> mappings = new HashMap<String, String>();
mappings.put("org.springframework.security.authentication.CredentialsExpiredException", "/admin/login?reset");
mappings.put("org.springframework.security.authentication.LockedException", "/admin/login?locked");
mappings.put("org.springframework.security.authentication.BadCredentialsException", "/admin/login?error");
mappings.put("org.springframework.security.core.userdetails.UsernameNotFoundException", "/admin/login?error");
ex.setExceptionMappings(mappings);
return ex;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider());
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**")
;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/login", "/admin/login/new**", "/admin/register", "/admin/logout", "/assets/**", "/admin/session/timeout").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().permitAll()
.and()
.formLogin()
.failureHandler(exceptionMappingAuthenticationFailureHandler())
.loginProcessingUrl("/login")
.loginPage("/admin/login")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/admin/orders")
.and()
.logout()
.logoutUrl("/logout")
.and()
.requiresChannel()
.antMatchers("/admin/**").requiresSecure()
.and()
.addFilterBefore(systemAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
Never mind, I fixed it by the changing the regex on the login processing url. It seemed to be interfering with the previous antmatcher.
So by changing the login processing url in the form login and custom filter configurations to "log_in", the login page now remains accessible without authorisation.

Resources