Spring Безопасность 3 - всегда возвращает ошибку 302

Я использую Spring 4 для создания простого приложения. В последнее время я добавляю Spring Security 3 в проект, но всегда получаю код ошибки 302 (поэтому он всегда перенаправляется на страницу home).

Вот мой SecurityConfig:

@Configuration
@EnableWebMvcSecurity
@ComponentScan(basePackages = { "com.moon.repository" })
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().withUser("hello").password("world").roles("USER");
}

@Override
public void configure(WebSecurity web) throws Exception {
    web
    .ignoring().antMatchers("/resources/**", "/views/**");
}

@Override
protected void configure(HttpSecurity http) throws Exception {

    http.authorizeRequests()
            .antMatchers("/","/home").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .loginPage("/home")
            .loginProcessingUrl("/acct/signin")
            .and()
            .logout()
            .permitAll();
}

}

У меня есть контроллер, называемый AccountController:

@Controller
@RequestMapping(value = "/acct")
public class AccountController {

private final Logger logger = LoggerFactory.getLogger(AccountController.class);

@RequestMapping(value = "/signin", method = RequestMethod.POST)
public String signin(@RequestParam("username") String username,
        @RequestParam("password") String password) {

    logger.info("======== [username:{0}][password:{1}] ========", username, password);

    if ("[email protected]".equalsIgnoreCase(username)) {
        return "error";
    } else {
        return "demo";
    }
}

}

Моя структура WEB-INF:

WEB-INF
----views
--------home.jsp
--------demo.jsp
--------error.jsp

Поток выглядит так:

  • Пользователь получает доступ к веб-сайту с помощью http://mylocal:8080/moon = > показывает home.jsp
  • Пользователь нажимает кнопку SignIn, и появляется всплывающее окно с запросом имени пользователя и пароля = > по-прежнему в home.jsp
  • Пользователь нажимает кнопку Submit = = Я предполагаю, что он пойдет /acct/signin и вернется в /demo, но я вижу Error 302 в Google Chrome, а затем он снова возвращается в /home

Любые идеи? Я застрял в течение 2 полных дней, и теперь я почти в отчаянии...

Большое вам спасибо, чтобы взглянуть на мою проблему.

================================= Первое обновление ============================

Обновление: форма в home.jsp

<form:form role="form" method="POST" action="acct/signin"
class="form-signin">
<div class="row">
    <div class="col-lg-5">
        <input name="username" size="20" type="email"
            class="form-control" placeholder="Email address" required
            autofocus> 
            <input name="password" type="password"
                    class="form-control" placeholder="Password" required>
                <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
    </div>
</div>
</form:form>

================================= Второе обновление ======= ============================

Я попытался реализовать UserDetailsService (не использовать auth в памяти), но все же... та же проблема - Ошибка 302

AppUserDetailsServiceImpl.java

@Component
public class AppUserDetailsServiceImpl implements UserDetailsService {

    private final Logger logger = LoggerFactory.getLogger(AppUserDetailsServiceImpl.class);

    @Override
    public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {

        logger.info("loadUserByUsername username=" + username);
        logger.info("======== {} ========",SecurityContextHolder.getContext().getAuthentication());

        if (!username.equals("hello")) {
            throw new UsernameNotFoundException(username + " not found");
        }

        // creating dummy user details
        return new UserDetails() {

            private static final long serialVersionUID = 2059202961588104658L;

            @Override
            public boolean isEnabled() {
                return true;
            }

            @Override
            public boolean isCredentialsNonExpired() {
                return true;
            }

            @Override
            public boolean isAccountNonLocked() {
                return true;
            }

            @Override
            public boolean isAccountNonExpired() {
                return true;
            }

            @Override
            public String getUsername() {
                return username;
            }

            @Override
            public String getPassword() {
                return "world";
            }

            @Override
            public Collection<? extends GrantedAuthority> getAuthorities() {
                List<SimpleGrantedAuthority> auths = new java.util.ArrayList<SimpleGrantedAuthority>();
                auths.add(new SimpleGrantedAuthority("USER"));
                return auths;
            }
        };
    }

В журнале отображается:

[14/08/19 15:16:32:200][INFO ][com.moon.repository.AppUserDetailsServiceImpl][loadUserByUsername](24) loadUserByUsername username=hello
[14/08/19 15:16:32:200][INFO ][com.moon.repository.AppUserDetailsServiceImpl][loadUserByUsername](25) ======== org.springframew[email protected]f1e4f742: Principal: [email protected]; Credentials: [PROTECTED]; Authenticated: true; Details: org.sprin[email protected]12afc: RemoteIpAddress: 127.0.0.1; SessionId: 023BC9A8B997ECBD826DD7C33AF55FC7; Granted Authorities: USER ========

Ответ 1

Я считаю, что Spring перенаправляет вас в /home потому что вы на самом деле не аутентифицировали пользователя через процесс входа в систему.

  1. Вы http://mylocal:8080/moon доступ к своему веб-приложению через http://mylocal:8080/moon возвращая представление home.jsp
  2. Вы нажимаете кнопку Войти, отправляя форму входа поскольку логин формы не объявляется явно, Spring Security отобразит окно с приглашением ввести имя пользователя и пароль, чтобы конечный пользователь мог ввести свои учетные данные
  3. Эти учетные данные затем отправляются на URL-адрес обработки входа (/acct/signin), для которого у вас есть сопоставление с методом signin в AccountController
  4. Такой контроллер не может аутентифицировать пользователя Spring, но все равно перенаправляет запрос в /demo, возвращая строку
  5. Путь /demo защищен (.anyRequest().authenticated()) для любого пользователя, не .anyRequest().authenticated() проверку подлинности, поскольку текущий пользователь действительно не прошел проверку подлинности, Spring Security автоматически перенаправит запрос на страницу входа
  6. Вы попали в /home (.loginPage("/home"))

Используя InMemoryUserDetailsManagerConfigurer (см. Javadoc inMemoryAuthentication), вы можете успешно войти только через настроенные учетные данные. Если вам нужна полноценная система аутентификации, вы должны предоставить реализацию UserDetailsService для вашей конфигурации Spring Security (через метод userDetailsService).


РЕДАКТИРОВАТЬ: После разговора с chialin.lin, кажется, отсутствующая конфигурация была defaultSuccessfulUrl для Spring Security, чтобы знать, куда перенаправить пользователя после аутентификации.

Ответ 2

Для меня я пришел из немного другого варианта использования, но у "внезапно" возникла та же проблема, прежде чем она отлично заработала.
Мой Setup Spring с внешним интерфейсом ExtJs, где я сейчас встраиваю интерфейс отдыха.
Все работало очень хорошо, и вдруг у меня появилось http 302 ответа (WTH?)

Поскольку я реализовал с помощью кода, следуя этому примеру: https://octoperf.com/blog/2018/03/08/securing-rest-api-spring-security/
существует объявление SimpleUrlAuthenticationSuccessHandler.
См. 4.4 SecurityConfig, где TokenAuthenticationFilter создается с классом NoRedirectStrategy; см. 4.1 Стратегия перенаправления

В свою очередь, если бы в моем расширении AbstractAuthenticationProcessingFilter не было настроено NoRedirectStrategy, было бы показано http 302 ответа.

Ответ 3

Для того, чтобы избежать того, чтобы создать новое тривиальное SuccessHandler, переопределить successfulAuthentication метод в фильтре и просто вызовите chain.doFilter() метод после установки Authentication объекта в контексте безопасности.