spring - 为什么 Spring Security + Angular 登录在 AuthenticationSuccessHandler 和 RestController 上有不同的会话?
问题描述
我在 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);
}
}
角度 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 来验证这一点。如果我是正确的,那么您将被分配一个新的会话。
推荐阅读
- python-3.x - 尝试在python中减去变量时无法分配给函数调用语法错误
- regex - .htaccess 重定向如果 # 存在于 URI 中
- java - 递归语句不立即返回
- python - 在树目录中检测到文件(用于重命名)但未重命名
- python - python中进程和主任务之间的共享内存
- apache - 不能在 URL 中使用 http/https 强制 www
- javascript - 使用三个文本框进行自定义验证
- javascript - 创建一个可以被多个组件读取和更新的共享日期服务
- sql - 索引列和日期列之间查询分区的性能
- delphi - 什么是绘制和平滑抗锯齿绘制对象并移动它们的最佳方法