为什么Spring Security + Angular登录在AuthenticationSuccessHandler和RestController上有不同的会话?

问题描述 投票:0回答:1

我在Angular中有一个Spring Security配置和一个登录页面。成功登录后,我的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);

}

}

angular 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控制器返回null,因此login.component.ts在订阅上收到null。

spring angular spring-boot spring-security spring-session
1个回答
0
投票

我认为这是因为如果你有一个成功的身份验证,Spring会“交换”你的会话,以防止某些攻击。

有人可以在未经身份验证的情况下“窃取”您的会话cookie,然后在您登录时使用它 - 使用您现在经过身份验证的会话访问受保护的资源。

如果你从未参加过会议 - 例如。当通过Postman执行登录请求时 - 在会话中从来没有一点你在哪里“不安全” - 所以Spring不必这样做。

您可以通过在邮递员中请求登录页面,复制您获得的sessionId并在登录请求中将其设置为session-cookie来验证这一点。如果我是正确的,那么您将被分配一个新的会话。

© www.soinside.com 2019 - 2024. All rights reserved.