Почему Spring Security + Angular Login имеют разные сессии на AuthenticationSuccessHandler и RestController? - PullRequest
0 голосов
/ 30 августа 2018

У меня есть конфигурация Spring Security и страница входа в Angular. После успешного входа в систему мой SimpleAuthenticationSuccessHandler перенаправляет меня на контроллер, который получает пользователя из сеанса и возвращает его. Когда я вызываю логин из Postman, все идет как положено, но когда я вызываю его из Chrome, он не работает, потому что сеанс на SimpleAuthenticationSuccessHandler отличается от сеанса, полученного на контроллере.

Это класс конфигурации для Spring Security:

@Configuration
@EnableWebSecurity
@ComponentScan("backend.configuration")
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableMongoRepositories(basePackages = "backend.repositories")
public class SecurityConfig extends WebSecurityConfigurerAdapter {


@Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().and().csrf().disable()
            .exceptionHandling()
            .authenticationEntryPoint(restAuthenticationEntryPoint)
            .and()
            .authorizeRequests()
            .antMatchers("/user/").authenticated()
            .and()
            .formLogin()
            .usernameParameter("email")
            .loginProcessingUrl("/login").
            successHandler(authenticationSuccessHandler())
            .failureHandler(new SimpleUrlAuthenticationFailureHandler())
            .and()
            .logout();
}
@Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
    return new SimpleOnSuccessAuthenticationHandler();
}

@Bean
CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
    configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
    UrlBasedCorsConfigurationSource source = new 
    UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

Это пользовательский обработчик успеха аутентификации:

public class SimpleOnSuccessAuthenticationHandler
    implements AuthenticationSuccessHandler {

protected Log logger = LogFactory.getLog(this.getClass());

@Autowired
UserRepository userRepository;

private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

@Override
public void onAuthenticationSuccess(HttpServletRequest request,
                                    HttpServletResponse response, 
                                    Authentication authentication)
        throws IOException {

    handle(request, response, authentication);
    clearAuthenticationAttributes(request);
}

protected void handle(HttpServletRequest request,
                      HttpServletResponse response, Authentication 
                                                 authentication)
        throws IOException {
    HttpSession session = request.getSession();
    ObjectId objectId = ((MongoUserDetails) 
    authentication.getPrincipal()).getId();
    User loggedUser = userRepository.findById(objectId).orElse(null);
    UserDto loggedUserDto = UserConverter.convertUserToDto(loggedUser);
    session.setAttribute("loggedUser", loggedUserDto);


    if (response.isCommitted()) {
        logger.debug(
                "Response has already been committed. Unable to redirect to "
                        + "/loginSuccess");
        return;
    }
    redirectStrategy.sendRedirect(request, response, "/loginSuccess");
}


protected void clearAuthenticationAttributes(HttpServletRequest request) {
    HttpSession session = request.getSession(false);
    if (session == null) {
        return;
    }
    session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
}

Это контроллер, который возвращает пользователя:

@CrossOrigin
@RestController
public class LoginController {
@Autowired
UserService userService;

@RequestMapping(value = "/loginSuccess", method = RequestMethod.GET, 
produces = "application/json")
@ResponseBody
public ResponseEntity<UserDto> login(HttpServletRequest request) {
    UserDto loggedUser= (UserDto) 
    request.getSession().getAttribute("loggedUser");
    System.out.println(request.getSession().getId());
    System.out.println(request.getSession().getCreationTime());
    return new ResponseEntity<>((UserDto) 
    request.getSession().getAttribute("loggedUser"), HttpStatus.OK);

}

}

Угловой auth.service.ts:

@Injectable({providedIn: 'root'})
export class AuthService {

  apiURL = environment.apiUrl;

  constructor(private http: HttpClient) {}

  login(username: string, password: string) {
  let body = new URLSearchParams();
  body.set('email', username);
  body.set('password', password);

  let options = {headers: new HttpHeaders().set('Content-Type', 
               'application/x-www-form-urlencoded')
                };

  return this.http.post(this.apiURL + 'login', body.toString(), options);
  } 

  logout() {localStorage.removeItem('currentUser');}
}

И login.component.ts:

@Component({selector: 'app-login',templateUrl: './login.component.html',
          styleUrls: ['./login.component.css']
         })
export class LoginComponent implements OnInit {

 user = {} as any;
 returnUrl: string;
 form: FormGroup;
 formSubmitAttempt: boolean;
 errorMessage: string = '';
 welcomeMessage: string = 'Welcome to CS_DemandResponse Application';
 url = '/add_user';

 token: string;

 constructor(
 private fb: FormBuilder,
 private authService: AuthService,
 private route: ActivatedRoute,
 private router: Router
 ) {
}

ngOnInit() {
 this.authService.logout();
 this.returnUrl = this.route.snapshot.queryParams.returnUrl || '/';
 this.form = this.fb.group({
   email: [AppConstants.EMPTY_STRING, Validators.email],
   password: [AppConstants.EMPTY_STRING, Validators.required]
 });
}

isFieldInvalid(field: string) {
 return (
   (!this.form.get(field).valid && this.form.get(field).touched) ||
   (this.form.get(field).untouched && this.formSubmitAttempt)
 );
}

login() {
 if (this.form.valid) {
   this.authService.login(this.user.email, this.user.password)
     .subscribe((currentUser) => {
      this.user=currentUser;
      if (this.user != null) {
        localStorage.setItem('userId', (<User>this.user).id.toString());
        if (this.user.autorities.get(0) === 'ROLE_ADMIN' ) {
          this.router.navigate(['/admin']);
        }
        if (this.user.autorities.get(0) === 'ROLE_USER') {
          // this.route.params.subscribe((params) => {
          //   localStorage.setItem('userId', params.id);
          // });
          this.router.navigate(['/today']);
        }
      } else {
        this.errorMessage = ('Invalid email or password');
        this.welcomeMessage = '';
      }
    });

  this.formSubmitAttempt = true;
 }
}

}

Контроллер / loginSuccess возвращает ноль, поэтому login.component.ts получает нуль при подписке.

1 Ответ

0 голосов
/ 30 августа 2018

Я предполагаю, что это потому, что Spring «обменивает» ваш сеанс на успешную аутентификацию, если она у вас была, для предотвращения определенных атак.

Кто-то может «украсть» ваш файл cookie сеанса, пока он не прошел проверку подлинности, а затем использовать его - при входе в систему - для доступа к защищенным ресурсам, используя ваш сеанс, прошедший проверку подлинности.

Если у вас никогда не было сессии - например. при выполнении запроса на вход в систему через почтальона - в сеансе никогда не было точки, где вы были бы «небезопасны» - поэтому Spring не должен этого делать.

Вы можете проверить это, запросив страницу входа в систему почтальона, скопировав полученный идентификатор сеанса и установив его как cookie-файл сеанса в своем запросе на вход. Если я прав, вам будет назначен новый сеанс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...