Java API return 格式正确 JSON 表示成功和失败,包括身份验证错误
Java APIs return well formatted JSON for success and failure including authentication errors
不是开发 API 方面的专家,因此需要来自社区的一些 help/advice。
我有一个使用 Java Spring MVC for APIs 的应用程序。我正在使用 ResponseEntity return 回复任何使用 APIs(第三方或 UI)的人。
我的例子 API
@Controller
@RequestMapping("/Test/")
public ResponseEntity<TestGroup> getTestsById(@PathVariable("id") Integer id) {
TestGroup testGroup = testService.getTestById(id); //calls a service that returns test from the db
if(testGroup != null) {
return new ResponseEntity(testGroup, HttpStatus.OK);
} else {
return new ResponseEntity(HttpStatus.NOT_FOUND);
}
}
我还有其他 API 与此类似。我的问题是是否有任何框架或方法可以使我的 APIs return JSON 以格式良好的 JSON 响应错误或成功。例子
{
"code": 400,
"message": "Bad Request",
"description": "There were no credentials found."
}
{
"code": 200,
"data": "{<JSON blob of the object to be returned>}"
}
此外,
在映射任何路由之前,已经实现了一个过滤器来检查会话信息(将它们视为 oauth 令牌)以进行身份验证。我也需要这个错误处理过程在那个阶段工作,所以如果有过期的令牌或无效的令牌,我会得到一个格式正确的 JSON。例子
{
"code": 401,
"message": "Unauthorized",
"description": "The token used in the request is incorrect or has expired."
}
现在我收到默认消息 Spring 的身份验证过滤器以 HTML
的形式抛出
<html><head><title>Apache Tomcat/7.0.50 - Error report</title><style><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 500 - Access is denied</h1><HR size="1" noshade="noshade"><p><b>type</b> Exception report</p><p><b>message</b> <u>Access is denied</u></p><p><b>description</b> <u>The server encountered an internal error that prevented it from fulfilling this request.</u></p><p><b>exception</b> <pre>org.springframework.security.access.AccessDeniedException: Access is denied
org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:206)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
com.test.security.CookieAuthenticationFilter.doFilter(CookieAuthenticationFilter.java:95)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
</pre></p><p><b>note</b> <u>The full stack trace of the root cause is available in the Apache Tomcat/7.0.50 logs.</u></p><HR size="1" noshade="noshade"><h3>Apache Tomcat/7.0.50</h3></body></html>
抱歉没有传达这一点,我没有为 API 使用 spring 安全性,我的 web.xml 过滤器看起来像这样
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>Webapp</display-name>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.test.spring.config</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.test.spring.config=</param-value>
</init-param>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
下面是安全配置的处理方式。
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
@Autowired
private AbstractConfiguration configurationManager;
public SecurityConfiguration() {
super(true);
}
@Override
@Bean
protected AuthenticationManager authenticationManager() {
return new CustomAuthenticationManager();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/health/**").permitAll().and().authorizeRequests()
.antMatchers("/complete/**").permitAll().and()
.addFilterBefore(cookieAuthenticationFilter(), ChannelProcessingFilter.class).authorizeRequests()
.antMatchers("/**").hasAuthority("tests");
}
@Bean
public GenericFilterBean cookieAuthenticationFilter() {
if (System.getProperty("noauth") != null) {
return new SecurityFilterMock();
} else {
return new CookieAuthenticationFilter(redisTemplate());
}
}
}
根据建议,如果您阅读了下文,我已经制作了以下入口点 class 来处理令牌身份验证并显示 JSON
public class TokenAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
private static final Logger log = LoggerFactory.getLogger(TokenAuthenticationEntryPoint.class);
@Override
public void commence(HttpServletRequest request,HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
log.info(String.format("Unauthorized access with session id '%s'",
request.getSession().getId()));
ErrorHandler errorResponse = new ErrorHandler();
// populate your response object with all the info you need
errorResponse.setCode(401);
errorResponse.setDescription("The token used in the request is incorrect or invalid.");
errorResponse.setMessage("Unauthorized");
ObjectMapper jsonMapper = new ObjectMapper();
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
PrintWriter out = response.getWriter();
out.print(jsonMapper.writeValueAsString(errorResponse));
}
}
如有任何建议或指示,我们将不胜感激
return JSON 格式错误与 Spring 身份验证你可以在这里看看我的回答:How to return JSON response for unauthorized AJAX calls instead of login page as AJAX response?
基本上,您必须调整身份验证机制并重新定义入口点以正确处理响应。
不是开发 API 方面的专家,因此需要来自社区的一些 help/advice。 我有一个使用 Java Spring MVC for APIs 的应用程序。我正在使用 ResponseEntity return 回复任何使用 APIs(第三方或 UI)的人。 我的例子 API
@Controller
@RequestMapping("/Test/")
public ResponseEntity<TestGroup> getTestsById(@PathVariable("id") Integer id) {
TestGroup testGroup = testService.getTestById(id); //calls a service that returns test from the db
if(testGroup != null) {
return new ResponseEntity(testGroup, HttpStatus.OK);
} else {
return new ResponseEntity(HttpStatus.NOT_FOUND);
}
}
我还有其他 API 与此类似。我的问题是是否有任何框架或方法可以使我的 APIs return JSON 以格式良好的 JSON 响应错误或成功。例子
{
"code": 400,
"message": "Bad Request",
"description": "There were no credentials found."
}
{
"code": 200,
"data": "{<JSON blob of the object to be returned>}"
}
此外, 在映射任何路由之前,已经实现了一个过滤器来检查会话信息(将它们视为 oauth 令牌)以进行身份验证。我也需要这个错误处理过程在那个阶段工作,所以如果有过期的令牌或无效的令牌,我会得到一个格式正确的 JSON。例子
{
"code": 401,
"message": "Unauthorized",
"description": "The token used in the request is incorrect or has expired."
}
现在我收到默认消息 Spring 的身份验证过滤器以 HTML
的形式抛出<html><head><title>Apache Tomcat/7.0.50 - Error report</title><style><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 500 - Access is denied</h1><HR size="1" noshade="noshade"><p><b>type</b> Exception report</p><p><b>message</b> <u>Access is denied</u></p><p><b>description</b> <u>The server encountered an internal error that prevented it from fulfilling this request.</u></p><p><b>exception</b> <pre>org.springframework.security.access.AccessDeniedException: Access is denied
org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:206)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
com.test.security.CookieAuthenticationFilter.doFilter(CookieAuthenticationFilter.java:95)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
</pre></p><p><b>note</b> <u>The full stack trace of the root cause is available in the Apache Tomcat/7.0.50 logs.</u></p><HR size="1" noshade="noshade"><h3>Apache Tomcat/7.0.50</h3></body></html>
抱歉没有传达这一点,我没有为 API 使用 spring 安全性,我的 web.xml 过滤器看起来像这样
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>Webapp</display-name>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.test.spring.config</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.test.spring.config=</param-value>
</init-param>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
下面是安全配置的处理方式。
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
@Autowired
private AbstractConfiguration configurationManager;
public SecurityConfiguration() {
super(true);
}
@Override
@Bean
protected AuthenticationManager authenticationManager() {
return new CustomAuthenticationManager();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/health/**").permitAll().and().authorizeRequests()
.antMatchers("/complete/**").permitAll().and()
.addFilterBefore(cookieAuthenticationFilter(), ChannelProcessingFilter.class).authorizeRequests()
.antMatchers("/**").hasAuthority("tests");
}
@Bean
public GenericFilterBean cookieAuthenticationFilter() {
if (System.getProperty("noauth") != null) {
return new SecurityFilterMock();
} else {
return new CookieAuthenticationFilter(redisTemplate());
}
}
}
根据建议,如果您阅读了下文,我已经制作了以下入口点 class 来处理令牌身份验证并显示 JSON
public class TokenAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
private static final Logger log = LoggerFactory.getLogger(TokenAuthenticationEntryPoint.class);
@Override
public void commence(HttpServletRequest request,HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
log.info(String.format("Unauthorized access with session id '%s'",
request.getSession().getId()));
ErrorHandler errorResponse = new ErrorHandler();
// populate your response object with all the info you need
errorResponse.setCode(401);
errorResponse.setDescription("The token used in the request is incorrect or invalid.");
errorResponse.setMessage("Unauthorized");
ObjectMapper jsonMapper = new ObjectMapper();
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
PrintWriter out = response.getWriter();
out.print(jsonMapper.writeValueAsString(errorResponse));
}
}
如有任何建议或指示,我们将不胜感激
return JSON 格式错误与 Spring 身份验证你可以在这里看看我的回答:How to return JSON response for unauthorized AJAX calls instead of login page as AJAX response?
基本上,您必须调整身份验证机制并重新定义入口点以正确处理响应。