当Web会话到期时,Spring Security将以403 HTTP状态响应。理想情况下,它会以401响应。未授权和禁止是不同的。如果存在有效会话,则对安全资源的请求应仅返回403,但用户仅具有对所述资源的权限。如果资源是安全的并且没有经过身份验证的会话,则Spring Security应返回401。
我的应用程序需要非常具体地区分这两个错误代码。
我的问题是,如何自定义此行为?关于我对401和403之间差异的论证,read this。
答案 0 :(得分:0)
以下是我的解决方案:
@Configuration
public class WebCtxConfig implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof SessionManagementFilter) {
SessionManagementFilter filter = (SessionManagementFilter) bean;
filter.setInvalidSessionStrategy(new InvalidSessionStrategy() {
@Override
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
});
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
答案 1 :(得分:0)
在Spring Boot 2.2.1中,我已使用从AuthenticationEntryPoint派生的类来完成此操作:
'urlManager' => [
'class' => 'yii\web\UrlManager',
'showScriptName' => false,
'enablePrettyUrl' => true,
'rules' => [
'/login' => 'site/login',
'/logout' => 'site/logout',
Yii::$app->language.'/'.'<controller:\w+>s'=>'<controller>/index',
Yii::$app->language.'/'.'<controller:\w+>/<id:\d+>/<title:\w+>'=>'<controller>/view',
Yii::$app->language.'/'.'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
Yii::$app->language.'/'.'<controller:\w+>/<action:\w+>/<id:\d+>/*'=>'<controller>/<action>',
],
],
在您的安全配置中(我有可通过OAuth2.0令牌访问的ResourceServer)
import java.io.IOException;
import javassist.NotFoundException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class AppAuthenticationEntryPoint implements AuthenticationEntryPoint{
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException auth) throws IOException, ServletException {
// 401
setResponseError(response, HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed");
}
@ExceptionHandler (value = {AccessDeniedException.class})
public void commence(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
// 403
setResponseError(response, HttpServletResponse.SC_FORBIDDEN, String.format("Access Denies: %s", accessDeniedException.getMessage()));
}
@ExceptionHandler (value = {NotFoundException.class})
public void commence(HttpServletRequest request, HttpServletResponse response, NotFoundException notFoundException) throws IOException {
// 404
setResponseError(response, HttpServletResponse.SC_NOT_FOUND, String.format("Not found: %s", notFoundException.getMessage()));
}
@ExceptionHandler (value = {Exception.class})
public void commence(HttpServletRequest request, HttpServletResponse response, Exception exception) throws IOException {
// 500
setResponseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, String.format("Internal Server Error: %s", exception.getMessage()));
}
private void setResponseError(HttpServletResponse response, int errorCode, String errorMessage) throws IOException{
response.setStatus(errorCode);
response.getWriter().write(errorMessage);
response.getWriter().flush();
response.getWriter().close();
}
}