16

I asked a question about latest spring framework, code based configuration here

initializer

public class AppInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { SecurityConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { MvcConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}

mvc config

    @EnableWebMvc
    @ComponentScan({ "com.appname.controller" })
    public class MvcConfig extends WebMvcConfigurerAdapter {
        @Bean
        public InternalResourceViewResolver viewResolver() {
            InternalResourceViewResolver resolver = new InternalResourceViewResolver();
            resolver.setPrefix("/WEB-INF/jsp/");
            resolver.setSuffix(".jsp");
            return resolver;
        }

@Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/res/**").addResourceLocations("/res/");
    }
    }

security config

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private CustomUserDetailsService customUserDetailsService;

public SecurityConfig() {
    customUserDetailsService = new CustomUserDetailsService();
}

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
        throws Exception {
    auth.inMemoryAuthentication().withUser("user").password("password")
            .roles("USER");
    auth.userDetailsService(customUserDetailsService);
}

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/res/**").permitAll()
            .and().authorizeRequests()
            .anyRequest().hasRole("USER")
            .and().formLogin().loginPage("/account/signin").permitAll()
            .and().logout().permitAll();
    }
}

security initializer

public class SecurityInitializer extends
        AbstractSecurityWebApplicationInitializer {

}

custom login

public class CustomUserDetailsService implements UserDetailsService {

    private AccountRepository accountRepository;

    public CustomUserDetailsService() {
        this.accountRepository = new AccountRepository();
    }

    @Override
    public UserDetails loadUserByUsername(String email)
            throws UsernameNotFoundException {

        Account account = accountRepository.getAccountByEmail(email);

        if (account == null) {
            throw new UsernameNotFoundException("Invalid email/password.");
        }

        Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        authorities.add(new SimpleGrantedAuthority("USER"));

        return new User(account.getEmail(), account.getPassword(), authorities);
    }
}

However, now I have new issue about custom login.

when post to j_spring_security_check, I will receive http 302.

I'm requesting /, but after sign in, it stays on the sign in page.

Because I'm using spring security 4.x version, and purely code based configuration, so I can't find more reference on internet. Can anyone help to figure out why.

EDIT

org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'securityConfig': 
Injection of autowired dependencies failed; 
nested exception is org.springframework.beans.factory.BeanCreationException:
Could not autowire field: 
private org.springframework.security.core.userdetails.UserDetailsService sg.mathschool.infra.SecurityConfig.userDetailsService; 
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type [org.springframework.security.core.userdetails.UserDetailsService] found for dependency: 
expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=userDetailsService)}

I changed CustomUserDetailsService

@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {

    private AccountRepository accountRepository;

    public CustomUserDetailsService() {
        this.accountRepository = new AccountRepository();
    }

    @Override
    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String email)
            throws UsernameNotFoundException {

        Account account = accountRepository.getAccountByEmail(email);

        if (account == null) {
            throw new UsernameNotFoundException("Invalid email/password.");
        }

        Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        authorities.add(new SimpleGrantedAuthority("USER"));

        return new User(account.getEmail(), account.getPassword(), authorities);
    }
}

and security config

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("userDetailsService")
    private UserDetailsService userDetailsService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.inMemoryAuthentication().withUser("user").password("password")
                .roles("USER");
        auth.userDetailsService(userDetailsService).passwordEncoder(
                passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/res/**").permitAll()
                .antMatchers("/account/**").permitAll().anyRequest()
                .hasRole("USER").and().formLogin().loginPage("/account/signin")
                .failureUrl("/account/signin?error").usernameParameter("email")
                .passwordParameter("password").and().logout()
                .logoutSuccessUrl("/account/signin?logout").and().csrf();

    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }
}
Community
  • 1
  • 1
Timeless
  • 7,338
  • 9
  • 60
  • 94
  • 302 is correct, since the j_spring_security_check will redirect you to your wellcome page. What do you expect it to return? – Nitek Apr 10 '15 at 06:39
  • @Nitek because I'm requesting /, but after sign in, it stays on the sign in page. – Timeless Apr 10 '15 at 06:40
  • i had similar same problem, have you try the `org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler` ? There you have a method called `onAuthenticationSuccess()` there you can call from the `HttpServletResponse ` the method sendRedirect with your redirect page. – Manu Zi Apr 17 '15 at 05:58
  • 1
    @ManuZi using xml based configuration everything so far is good. And a lot of sample code available :) – Timeless Apr 17 '15 at 06:05
  • With regards to your autowiring exception, you are scanning for components in the package containing your user details service? – ConMan Apr 20 '15 at 08:01
  • can you tell me currently what error you supposed to get after the changes made? – Vigneshwaran Apr 20 '15 at 09:01
  • its class CustomUserDetailsService this in com.appname.controller package ? you tested change `@Qualifier("userDetailsService") UserDetailsService` to `@Qualifier("userDetailsService") CustomUserDetailsService ` in your _web.xml_ the dispatcher mvc is configured correctly. ? – Dexter Apr 20 '15 at 13:00

4 Answers4

9

In Spring Security 4.x login URL has changed to login instead of j_spring_security_check, see Migrating from Spring Security 3.x to 4.x (XML Configuration).

<form name='f'action="login" method='POST'>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
    <table>
        <tbody>
            <tr>
                <td>User Name</td>
                <td><input type="text" name="username" size="30" /></td>
            </tr>
            <tr>
                <td>Password</td>
                <td><input type="password" name="password" size="30" /></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" value="login" /></td>
            </tr>
        </tbody>
    </table>
</form>
dur
  • 15,689
  • 25
  • 79
  • 125
Yasitha Bandara
  • 1,995
  • 2
  • 14
  • 20
2

Maybe it's CORS issue ? whatever it is , you can check the request and the response by adding:

authentication-success-handler-ref="appLoginSuccessHandler" 
authentication-failure-handler-ref="appLoginFailureHandler"

to your spring security. it's should look like this:

<http use-expressions="true" disable-url-rewriting="true" >
    <logout invalidate-session="true" delete-cookies="true"/>
    <form-login login-page="/YOUR_login_PAGE"
        username-parameter="j_username"
        password-parameter="j_password"
        login-processing-url="/j_spring_security_check"
        authentication-failure-url="/YOUR_login_PAGE"
        default-target-url="/YOUR_login_PAGE" 
        authentication-success-handler-ref="appLoginSuccessHandler" 
        authentication-failure-handler-ref="appLoginFailureHandler"/>

It will call the correct methods on the appLoginSuccessHandler and appLoginFailureHandler services.

Example of service declaration:

@Service("appLoginSuccessHandler")
public class LoginSuccessHandler extends 
SimpleUrlAuthenticationSuccessHandler{

@Override
public void onAuthenticationSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication 
auth) throws IOException, ServletException{ ......
.....
.....
.....  Here you can handle also CORS ... and more ...
Alon Asulin
  • 109
  • 1
  • 3
1

it is defaultSuccessUrl("/")

   formLogin()
        ...
        .defaultSuccessUrl("/")
   ...
Ralph
  • 118,862
  • 56
  • 287
  • 383
  • I tried just now, still doesn't work. when I was trying to sign in, it stays on sign in page. So I tried to request /, it will be reditect to sign in page. So which means the authentication failed somewhere. – Timeless Apr 10 '15 at 06:57
0

Try also exactly specify "loginProcessingUrl":

formLogin()
   ...
   .loginProcessingUrl("/j_spring_security_check")
...