【问题标题】:How to secure REST API with Spring Boot and Spring Security?如何使用 Spring Boot 和 Spring Security 保护 REST API?
【发布时间】:2015-12-09 11:38:25
【问题描述】:

我知道保护 REST API 是一个广受评论的话题,但我无法创建一个符合我的标准的小型原型(我需要确认这些标准是现实的)。如何保护资源以及如何使用 Spring 安全性有很多选择,我需要澄清我的需求是否现实。

我的要求

  • 基于令牌的身份验证器 - 用户将提供其凭据并获得唯一且有时间限制的访问令牌。我想在自己的实现中管理令牌创建、检查有效性、过期。
  • 一些 REST 资源将是公开的 - 根本不需要进行身份验证,
  • 某些资源只能由具有管理员权限的用户访问,
  • 其他资源在所有用户授权后都可以访问。
  • 我不想使用基本身份验证
  • Java 代码配置(不是 XML)

当前状态

我的 REST API 运行良好,但现在我需要保护它。当我在寻找解决方案时,我创建了一个javax.servlet.Filter 过滤器:

  @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;

        String accessToken = request.getHeader(AUTHORIZATION_TOKEN);
        Account account = accountDao.find(accessToken);

        if (account == null) {    
            throw new UnauthorizedException();    
        }

        chain.doFilter(req, res);

    }

但是 javax.servlet.filters 的这个解决方案不能按我的需要工作,因为通过 @ControllerAdvice 和 Spring servlet dispatcher 处理异常存在问题。

我需要什么

我想知道这些标准是否现实并获得任何帮助,如何开始使用 Spring Security 保护 REST API。我阅读了许多教程(例如 Spring Data REST + Spring Security),但都在非常基本的配置中工作 - 他们的凭据存储在内存中配置中的用户,我需要使用 DBMS 并创建自己的身份验证器。

请给我一些建议如何开始。

【问题讨论】:

    标签: java spring rest spring-security


    【解决方案1】:

    基于令牌的身份验证 - 用户将提供其凭据并获取 唯一且有时间限制的访问令牌。我想管理令牌 在我自己的实现中创建、检查有效性、过期。

    实际上,使用过滤器进行令牌验证 - 在这种情况下最好的方法

    最终,您可以通过 Spring Data 创建 CRUD 来管理 Token 的属性,例如过期等。

    这是我的令牌过滤器: http://pastebin.com/13WWpLq2

    和令牌服务实现

    http://pastebin.com/dUYM555E

    一些 REST 资源将是公开的 - 根本不需要进行身份验证

    没问题,您可以通过 Spring 安全配置管理您的资源,如下所示:.antMatchers("/rest/blabla/**").permitAll()

    某些资源只能由具有管理员权限的用户访问,

    看看@Secured注解到类。示例:

    @Controller
    @RequestMapping(value = "/adminservice")
    @Secured("ROLE_ADMIN")
    public class AdminServiceController {
    

    其他资源在所有用户授权后都可以访问。

    回到 Spring Security 配置,你可以像这样配置你的 url:

        http
                .authorizeRequests()
                .antMatchers("/openforall/**").permitAll()
                .antMatchers("/alsoopen/**").permitAll()
                .anyRequest().authenticated()
    

    我不想使用基本身份验证

    是的,通过令牌过滤器,您的用户将通过身份验证。

    Java 代码配置(不是 XML)

    回到上面的话,看@EnableWebSecurity。 你的班级将是:

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {}
    

    您必须重写 configure 方法。下面的代码,仅举例说明如何配置匹配器。它来自另一个项目。

        @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/assets/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                    .usernameParameter("j_username")
                    .passwordParameter("j_password")
                    .loginPage("/login")
                    .defaultSuccessUrl("/", true)
                    .successHandler(customAuthenticationSuccessHandler)
                    .permitAll()
                .and()
                    .logout()
                    .logoutUrl("/logout")
                    .invalidateHttpSession(true)
                    .logoutSuccessUrl("/")
                    .deleteCookies("JSESSIONID")
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .and()
                    .csrf();
    }
    

    【讨论】:

    • 你能帮我解决我的问题吗? stackoverflow.com/questions/46065063/…
    • 任何包含所有需求的示例项目?
    • @Oleksandr:那是一个很长的镜头,但你能告诉我为什么你在 RESTAuthenticationTokenProcessingFilter 类的 updateLastLogin(...) 方法中启动了一个线程吗?
    • @z3d4s,实际上这是一个旧示例(4 年),现在我建议使用 OffsetDateTime,另一种方法等 :) 我建议使用新线程来减少用户请求的处理时间,因为在保存到数据库期间可能需要额外的时间。
    • 啊,我明白了,这是一个天才的解决方案!谢谢!
    【解决方案2】:

    Spring 安全性对于向 REST URL 提供身份验证和授权也非常有用。我们不需要指定任何自定义实现。

    首先,您需要在安全配置中指定restAuthenticationEntryPoint 的入口点引用,如下所示。

     <security:http pattern="/api/**" entry-point-ref="restAuthenticationEntryPoint" use-expressions="true" auto-config="true" create-session="stateless" >
    
        <security:intercept-url pattern="/api/userList" access="hasRole('ROLE_USER')"/>
        <security:intercept-url pattern="/api/managerList" access="hasRole('ROLE_ADMIN')"/>
        <security:custom-filter ref="preAuthFilter" position="PRE_AUTH_FILTER"/>
    </security:http>
    

    restAuthenticationEntryPoint 的实现可能如下。

     @Component
    public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
    
       public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException ) throws IOException {
          response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized" );
       }
    }
    

    在这之后你需要指定RequestHeaderAuthenticationFilter。它包含 RequestHeader 键。这基本上用于识别用户的身份验证。通常 RequestHeader 在进行 REST 调用时会携带此信息。 例如考虑下面的代码

       <bean id="preAuthFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
        <property name="principalRequestHeader" value="Authorization"/>
        <property name="authenticationManager" ref="authenticationManager" />
      </bean>
    

    这里,

    <property name="principalRequestHeader" value="Authorization"/>
    

    “授权”是传入请求的密钥。它保存所需的用户身份验证信息。 您还需要配置 PreAuthenticatedAuthenticationProvider 以满足我们的要求。

       <bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
    <property name="preAuthenticatedUserDetailsService">
      <bean id="userDetailsServiceWrapper"
          class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
        <property name="userDetailsService" ref="authenticationService"/>
      </bean>
    </property>
    </bean>
    

    此代码将通过身份验证和授权来保护 REST URL,而无需任何自定义实现。

    完整代码请点击以下链接:

    https://github.com/srinivas1918/spring-rest-security

    【讨论】:

      【解决方案3】:

      我也搜索了很长时间。我正在做一个类似的项目。我发现Spring有一个模块可以通过redis实现会话。它看起来简单而有用。 我也会添加到我的项目中。可能会有所帮助:

      http://docs.spring.io/spring-session/docs/1.2.1.BUILD-SNAPSHOT/reference/html5/guides/rest.html

      【讨论】:

        【解决方案4】:

        验证 REST API 有两种方法

        1 - 使用 application.properties 文件中设置的默认用户名和密码进行基本身份验证

        Basic Authentication

        2 - 使用带有实际用户名和密码的数据库 (userDetailsS​​ervice) 进行身份验证

        Advanced Authentication

        【讨论】:

        • 视频很有用。如何对 ReST API 进行相同的高级身份验证。这里只描述Web。是否有 REST API 中高级身份验证的视频教程。
        • 如果您看到了第二个视频(高级身份验证),那么我也在使用 REST 客户端(用于 REST API)进行相同的身份验证。
        猜你喜欢
        • 2014-02-21
        • 2016-02-23
        • 2014-04-13
        • 2014-07-29
        • 2016-12-30
        • 2014-12-06
        • 2013-01-26
        • 2013-11-29
        • 2014-11-03
        相关资源
        最近更新 更多