Java API返回格式良好的JSON,包括成功和失败,包括身份验证错误

时间:2015-10-27 23:19:55

标签: java rest spring-security spring-java-config

不是开发API的专家,因此需要社区提供一些帮助/建议。 我有一个使用Java Spring MVC for API的应用程序。我正在使用ResponseEntity将响应返回给任何使用API​​(第三方或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。我的问题是,是否有任何框架或方法,以便我可以让我的API返回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 tokes)以进行身份​​验证。我也需要这个错误处理过程在那个阶段工作,这样如果有一个过期的令牌或无效的令牌,我会得到一个格式良好的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>

为不通信而道歉,我没有使用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());
        }
    }
}

根据建议,如果你在下面阅读,我已经做了以下EntryPoint类来处理令牌认证并显示一个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));
}
}

任何建议或指示都会非常感激

1 个答案:

答案 0 :(得分:2)

要将JSON格式的错误返回到使用Spring进行身份验证,您可以在此处查看我的答案:How to return JSON response for unauthorized AJAX calls instead of login page as AJAX response?

基本上,您必须调整身份验证机制并重新定义入口点以正确处理响应。