我在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会“交换”你的会话,以防止某些攻击。
有人可以在未经身份验证的情况下“窃取”您的会话cookie,然后在您登录时使用它 - 使用您现在经过身份验证的会话访问受保护的资源。
如果你从未参加过会议 - 例如。当通过Postman执行登录请求时 - 在会话中从来没有一点你在哪里“不安全” - 所以Spring不必这样做。
您可以通过在邮递员中请求登录页面,复制您获得的sessionId并在登录请求中将其设置为session-cookie来验证这一点。如果我是正确的,那么您将被分配一个新的会话。