我注意到我的ResponseEntityExceptionHandler
不能处理Spring Security在Spring Boot应用程序中引发的异常。
但是,我需要一种方法来捕获invalidgrantexception
,当用户帐户仍然被禁用时,它似乎会被抛出。
用例很简单:如果一个用户当前被禁用,我想抛出一个适当的错误给我的客户机S.T。它可以相应地显示一条消息。
现在Spring Security的回应是
{
error: "invalid_grant",
error_description: "User is disabled"
}
我看到了这个问题,但由于某种原因,我的AuthFailureHandler
没有被调用:
@Configuration
@EnableResourceServer
public class ResourceServer extends ResourceServerConfigurerAdapter {
@Component
public class AuthFailureHandler implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
// Never reached ..
System.out.println("Hello World!");
}
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(customAuthEntryPoint());;
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.authenticationEntryPoint(customAuthEntryPoint());
}
@Bean
public AuthenticationEntryPoint customAuthEntryPoint(){
return new AuthFailureHandler();
}
}
知道我错过了什么吗?
这是我的OAuth2Configuration
,它还包含一个ResourceServerConfiguraerAdapter
,它应该处理该异常
@Configuration
@EnableAuthorizationServer
@EnableResourceServer
public class OAuth2Configuration extends AuthorizationServerConfigurerAdapter {
private final TokenStore tokenStore;
private final JwtAccessTokenConverter accessTokenConverter;
private final AuthenticationManager authenticationManager;
@Autowired
public OAuth2Configuration(TokenStore tokenStore, JwtAccessTokenConverter accessTokenConverter, AuthenticationManager authenticationManager) {
this.tokenStore = tokenStore;
this.accessTokenConverter = accessTokenConverter;
this.authenticationManager = authenticationManager;
}
@Value("${security.jwt.client-id}")
private String clientId;
@Value("${security.jwt.client-secret}")
private String clientSecret;
@Value("${security.jwt.scope-read}")
private String scopeRead;
@Value("${security.jwt.scope-write}")
private String scopeWrite;
@Value("${security.jwt.resource-ids}")
private String resourceIds;
private final static String WEBHOOK_ENDPOINTS = "/r/api/*/webhooks/**";
private final static String PUBLIC_ENDPOINTS = "/r/api/*/public/**";
@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
configurer
.inMemory()
.withClient(clientId)
.secret(clientSecret)
.scopes(scopeRead, scopeWrite)
.resourceIds(resourceIds)
.accessTokenValiditySeconds(60*60*24*7 * 2) // Access tokens last two weeks
.refreshTokenValiditySeconds(60*60*24*7 * 12) // Refresh tokens last 12 weeks
//.accessTokenValiditySeconds(5)
//.refreshTokenValiditySeconds(10)
.authorizedGrantTypes("password", "refresh_token"); //, "client_credentials");
}
/**
* Since there are currently multiply clients, we map the OAuth2 endpoints under /api
* to avoid conflicts with @{@link ForwardController}
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
enhancerChain.setTokenEnhancers(Collections.singletonList(accessTokenConverter));
endpoints.tokenStore(tokenStore)
// Create path mappings to avoid conflicts with forwarding controller
.pathMapping("/oauth/authorize", "/api/v1/oauth/authorize")
.pathMapping("/oauth/check_token", "/api/v1/oauth/check_token")
.pathMapping("/oauth/confirm_access", "/api/v1/oauth/confirm_access")
.pathMapping("/oauth/error", "/api/v1/oauth/error")
.pathMapping("/oauth/token", "/api/v1/oauth/token")
.accessTokenConverter(accessTokenConverter)
.tokenEnhancer(enhancerChain)
.reuseRefreshTokens(false)
.authenticationManager(authenticationManager);
}
/**
* Forwarding controller.
*
* This controller manages forwarding in particular for the static web clients. Since there are multiple
* clients, this controller will map <i>any</i> GET request to the root /* to one of the clients.
*
* If no match was found, the default redirect goes to /web/index.html
*
*/
@Controller
public class ForwardController {
@RequestMapping(value = "/sitemap.xml", method = RequestMethod.GET)
public String redirectSitemapXml(HttpServletRequest request) {
return "forward:/a/web/assets/sitemap.xml";
}
@RequestMapping(value = "/robots.txt", method = RequestMethod.GET)
public String redirectRobotTxt(HttpServletRequest request) {
return "forward:/a/web/assets/robots.txt";
}
@RequestMapping(value = "/*", method = RequestMethod.GET)
public String redirectRoot(HttpServletRequest request) {
return "forward:/a/web/index.html";
}
@RequestMapping(value = "/a/**/{path:[^.]*}", method = RequestMethod.GET)
public String redirectClients(HttpServletRequest request) {
String requestURI = request.getRequestURI();
if (requestURI.startsWith("/a/admin/")) {
return "forward:/a/admin/index.html";
}
if (requestURI.startsWith("/a/swagger/")) {
return "forward:/a/swagger/swagger-ui.html#/";
}
return "forward:/a/web/index.html";
}
}
@Configuration
@EnableResourceServer
public class ResourceServer extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.requiresChannel()
/* Require HTTPS evereywhere*/
.antMatchers("/**")
.requiresSecure()
.and()
.exceptionHandling()
.and()
/* Permit all requests towards the public api as well as webhook endpoints. */
.authorizeRequests()
.antMatchers(PUBLIC_ENDPOINTS, WEBHOOK_ENDPOINTS)
.permitAll()
/* Required for ForwardController */
.antMatchers(HttpMethod.GET, "/*")
.permitAll()
.antMatchers("/r/api/**")
.authenticated();
// @formatter:on
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources
.resourceId(resourceIds)
.authenticationEntryPoint(customAuthEntryPoint());
}
@Component
public class AuthFailureHandler implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
System.out.println("Hello");
// FIXME We need to return HTTP 401 (s.t. the client knows what's going on) but for some reason it's not working as intended!
throw authException;
}
}
@Bean
public AuthenticationEntryPoint customAuthEntryPoint(){
return new AuthFailureHandler();
}
}
}
此外,这里还有WebSecurityConfigurerAdapter
尽管我认为它在这里不起作用:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${security.signing-key}")
private String signingKey;
private final UserDetailsService userDetailsService;
private final DataSource dataSource;
@Autowired
public WebSecurityConfig(@Qualifier("appUserDetailsService") UserDetailsService userDetailsService, DataSource dataSource) {
this.userDetailsService = userDetailsService;
this.dataSource = dataSource;
}
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Override
public void configure(WebSecurity web) throws Exception {
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(signingKey);
return converter;
}
/**
* Using {@link JwtTokenStore} for JWT access tokens.
* @return
*/
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
/**
* Provide {@link DefaultTokenServices} using the {@link JwtTokenStore}.
* @return
*/
@Bean
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
/**
* We provide the AuthenticationManagerBuilder using our {@link UserDetailsService} and the {@link BCryptPasswordEncoder}.
* @param auth
* @throws Exception
*/
@Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(this.userDetailsService)
.passwordEncoder(passwordEncoder())
.and()
.authenticationProvider(daoAuthenticationProvider());
}
/**
* Using {@link BCryptPasswordEncoder} for user-password encryption.
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* Provide {@link DaoAuthenticationProvider} for password encoding and set the {@link UserDetailsService}.
* @return
*/
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
daoAuthenticationProvider.setUserDetailsService(this.userDetailsService);
return daoAuthenticationProvider;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requiresChannel()
.requestMatchers(r -> r.getHeader("X-Forwarded-Proto") != null)
.requiresSecure();
}
}
创建扩展ResponseEntityExceptionHandler的自定义类,使用@ControllerAdvice
对其进行注释,并处理OAuth2Exception
(InvalidGrantException
的基类)。
@ExceptionHandler({OAuth2Exception.class})
public ResponseEntity<Object> handleOAuth2Exception(OAuth2Exception exception, WebRequest request) {
LOGGER.debug("OAuth failed on request processing", exception);
return this.handleExceptionInternal(exception, ErrorOutputDto.create(exception.getOAuth2ErrorCode(), exception.getMessage()), new HttpHeaders(), HttpStatus.valueOf(exception.getHttpErrorCode()), request);
}
使用HandlerExceptionResolverComposite将系统中的所有异常解析器复合为一个异常解析器。这将覆盖WebMVCConfigurationSupport
类中定义的相应bean。添加ExceptionResolvers
列表,如DefaulTerrorAttributes
和ExceptionHandlerExceptionResolver
(这通过涉及具有@ControllerAdvice
注释的类,例如我们创建的扩展ResponseEntityExceptionHandler的自定义类,从而启用基于AOP的异常处理。
<bean id="handlerExceptionResolver" class="org.springframework.web.servlet.handler.HandlerExceptionResolverComposite">
<property name="exceptionResolvers">
<list>
<bean class="org.springframework.boot.web.servlet.error.DefaultErrorAttributes"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver">
<property name="messageConverters">
<list>
<ref bean="jackson2HttpMessageConverter" />
</list>
</property>
</bean>
</list>
</property>
问题内容: 网页上的数据是动态显示的,似乎检查html中的每个更改并提取数据是一项非常艰巨的任务,并且还需要我使用非常不可靠的XPath。因此,我希望能够从数据包中提取数据。 我希望能够从数据包中提取信息以及生成要发送到服务器的“ XHR”数据包。提取信息部分对我来说更重要,因为通过使用casperjs自动触发html元素可以轻松处理信息的发送。 我附上我的意思的屏幕截图。 响应选项卡中的文本是我
我目前在我的路由中使用dotry/doCatch块,因此我无法使用全局onException块。 然而,如果驼峰路由中断(由于错误代码或意外/未测试的场景),我希望执行一些业务逻辑。希望这永远不会发生,但我仍然想处理更糟糕的情况。 我不能在全局OneException块中有java.lang.Exception,而且,我不想在每个路由上都添加一个额外的捕获。 在抛出未捕获的异常和中断路由之前,是否
我正在使用AWS SNS向移动应用发送推送通知,订阅单个SNS主题下的所有设备。然后,为了发送通知,我只发送到该主题。然而,如果某个endpoint由于任何原因无法接收消息,SNS会将该endpoint标记为禁用。 如果SNS只标记一个永久无效的endpoint,这可能很好,但事实并非如此。因为如果我重新启用它,那么在下一次消息推送后,它可能不会返回再次禁用,这意味着在下一次推送中它可以正确接收消
问题内容: 我有此代码- 编译器将如何实际实现这一点。实际在汇编代码中生成的异常检查在哪里? 更新 我知道上面的代码如何转换为 bytecode 。字节码仅将try-catch转换为相应的try- handler块。我对它将如何转换为jvm进行汇编和/或处理感兴趣。 问题答案: 试捕法的成本 大致来说,block不会向结果程序集中添加任何异常检查代码。只要不引发异常,它基本上是无操作的。所有缓慢的
我希望捕捉一些jackson异常,这些异常发生在我正在开发的spring-boot API中。例如,我有下面的request类,我想捕获当JSON request对象中的“QuarmissionAireResponse”键为null或空白(即request对象中的)时发生的错误。 导致此Jackson错误: 是否有一种方法来捕获和处理这些异常(在示例中JsonRootName是null/inval
问题内容: AngularJS依赖于启用的JavaScript。这意味着,如果有人访问用AngularJS内置的应用程序或网站,它将无法正确呈现。 使用AngularJS时,用于处理禁用了JavaScript的访客的常用约定是什么? 请解释为什么会这样? 问题包括处理有角度的JS指令和{{data_bindings}}。这样,当页面无法呈现页面时,数据就不会显示。 问题答案: 在考虑了该问题的其他