Springboot和Spring Security中认证成功后如何授权Rest API调用

Posted

技术标签:

【中文标题】Springboot和Spring Security中认证成功后如何授权Rest API调用【英文标题】:How to Authorize Rest API call after the successful authentication in Springboot and Spring Security 【发布时间】:2020-02-08 19:45:15 【问题描述】:

我有一个使用 Springboot 开发的 Restful API。另外,我已经实现了外部 ldap 身份验证。 http://localhost:8080/login?username=test&password=test 通过 ldap 身份验证,并按预期正常工作。

我的 Spring 安全实现如下。我正在限制所有 API 调用,需要进行身份验证。

@Configuration
@EnableWebSecurity
public class ApplicationSecurity extends WebSecurityConfigurerAdapter 


    @Override
    protected void configure(HttpSecurity http) throws Exception 

        http
        .csrf().disable()
        .authorizeRequests()
        .antMatchers("/login").permitAll()
        .anyRequest().authenticated()        
        .and().sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    
...
...

我的登录请求有效,所有 API 调用都没有通过身份验证,即使在成功登录后也是如此。我可以看到日志显示 ldap 身份验证成功并创建了 SecurityContext。而且我的下一个 API 调用没有在上一个登录请求中创建这个 SecurityContext。 我的日志如下。

2019-10-11 02:22:31.567 调试 39216 --- [nio-8080-exec-1] os.security.web.FilterChainProxy : /login?username=test&password=test 在 11 的位置 5 附加 过滤链;触发过滤器:'UsernamePasswordAuthenticationFilter' 2019-10-11 02:22:31.567 调试 39216 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher :检查请求的匹配: '/登录';针对 '/login' 2019-10-11 02:22:31.567 DEBUG 39216 --- [nio-8080-exec-1] w.a.UsernamePasswordAuthenticationFilter : 请求 是处理身份验证 2019-10-11 02:22:31.570 DEBUG 39216 --- [nio-8080-exec-1] os.s.authentication.ProviderManager : 身份验证尝试使用 portal.services.security.CustomActiveDirectoryLdapAuthenticationProvider 2019-10-11 02:22:32.767 调试 39216 --- [nio-8080-exec-1] o.s.s.ldap.SpringSecurityLdapTemplate :搜索下的条目 DN '', base = 'dc=abc,dc=com', filter = '(&(objectClass=Person) ((sAMAccountName=12323)))' 2019-10-11 02:22:32.777 调试 39216 --- [nio-8080-exec-1] os.s.ldap.SpringSecurityLdapTemplate:找到 DN: CN=测试名称,OU=用户,DC=com 2019-10-11 02:22:32.779 INFO 39216 --- [nio-8080-exec-1] os.s.ldap.SpringSecurityLdapTemplate:忽略 PartialResultException 2019-10-11 02:22:32.896 调试 39216 --- [nio-8080-exec-1] os.s.l.u.LdapUserDetailsMapper:映射 DN 上下文中的用户详细信息:CN=test name,OU=Users,DC=com 2019-10-11 02:22:32.899 调试 39216 --- [nio-8080-exec-1] s.CompositeSessionAuthenticationStrategy :委托给 org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy@45a4b042 2019-10-11 02:22:32.899 调试 39216 --- [nio-8080-exec-1] w.a.UsernamePasswordAuthenticationFilter : 认证成功。 更新 SecurityContextHolder 以包含: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@ff0ce9d1: 主要的: org.springframework.security.ldap.userdetails.LdapUserDetailsImpl@7a480d2: Dn:CN=测试名称,OU=用户,DC=com;用户名:test;密码: [受保护];启用:真; AccountNonExpired:真; CredentialsNonExpired:真; AccountNonLocked:真;的确 权限:管理员;凭证:[受保护];已认证:真实; 详细信息:用户;授予权限:管理员 2019-10-11 02:22:33.117 调试 39216 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter :不注入 HSTS 标头 因为它与 requestMatcher 不匹配 org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@17741df1 2019-10-11 02:22:33.117 调试 39216 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder 现在 清除,因为请求处理完成 2019-10-11 02:22:33.650 DEBUG 39216 --- [nio-8080-exec-2] os.security.web.FilterChainProxy : /admin/getCutOffDays 位于附加过滤器链中第 11 个位置的位置; 触发过滤器:'WebAsyncManagerIntegrationFilter' 2019-10-11 02:22:33.650 调试 39216 --- [nio-8080-exec-2] os.security.web.FilterChainProxy:/admin/getCutOffDays at 附加过滤器链中 11 个中的第 2 位;发射过滤器: 'SecurityContextPersistenceFilter' 2019-10-11 02:22:33.654 调试 39216 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /admin/getCutOffDays 位于附加过滤器链中第 11 位的第 3 位; 触发过滤器:'HeaderWriterFilter' 2019-10-11 02:22:33.654 DEBUG 39216 --- [nio-8080-exec-2] os.security.web.FilterChainProxy : /admin/getCutOffDays 位于附加过滤器链中第 11 位的第 4 位; 触发过滤器:'LogoutFilter' 2019-10-11 02:22:33.654 DEBUG 39216 --- [nio-8080-exec-2] os.s.web.util.matcher.OrRequestMatcher:试图 匹配使用 Ant [pattern='/logout', GET] 2019-10-11 02:22:33.655 DEBUG 39216 --- [nio-8080-exec-2] os.s.w.u.matcher.AntPathRequestMatcher : 请求“OPTIONS /admin/getCutOffDays”与“GET /logout”不匹配 2019-10-11 02:22:33.655 调试 39216 --- [nio-8080-exec-2] os.s.web.util.matcher.OrRequestMatcher :尝试使用 Ant 进行匹配 [pattern='/logout', POST] 2019-10-11 02:22:33.655 调试 39216 --- [nio-8080-exec-2] os.s.w.u.matcher.AntPathRequestMatcher:请求 'OPTIONS /admin/getCutOffDays' 不匹配 'POST /logout 2019-10-11 02:22:33.655 调试 39216 --- [nio-8080-exec-2] os.s.web.util.matcher.OrRequestMatcher :尝试使用 Ant 进行匹配 [pattern='/logout', PUT] 2019-10-11 02:22:33.655 调试 39216 --- [nio-8080-exec-2] os.s.w.u.matcher.AntPathRequestMatcher:请求 'OPTIONS /admin/getCutOffDays' 不匹配 'PUT /logout 2019-10-11 02:22:33.656 调试 39216 --- [nio-8080-exec-2] os.s.web.util.matcher.OrRequestMatcher :尝试使用 Ant 进行匹配 [模式='/注销',删除] 2019-10-11 02:22:33.656 调试 39216 --- [nio-8080-exec-2] os.s.w.u.matcher.AntPathRequestMatcher:请求 'OPTIONS /admin/getCutOffDays' 不匹配 'DELETE /logout 2019-10-11 02:22:33.656 调试 39216 --- [nio-8080-exec-2] os.s.web.util.matcher.OrRequestMatcher:未找到匹配项 2019-10-11 02:22:33.657 调试 39216 --- [nio-8080-exec-2] os.security.web.FilterChainProxy:/admin/getCutOffDays at 附加过滤器链中的 11 位中的第 5 位;发射过滤器: 'UsernamePasswordAuthenticationFilter' 2019-10-11 02:22:33.657 调试 39216 --- [nio-8080-exec-2] os.s.w.u.matcher.AntPathRequestMatcher : 请求“OPTIONS /admin/getCutOffDays”与“POST /login”不匹配 2019-10-11 02:22:33.657 调试 39216 --- [nio-8080-exec-2] os.security.web.FilterChainProxy:/admin/getCutOffDays at 附加过滤器链中 11 个中的第 6 位;发射过滤器: 'RequestCacheAwareFilter' 2019-10-11 02:22:33.657 调试 39216 --- [nio-8080-exec-2] os.security.web.FilterChainProxy : /admin/getCutOffDays 位于附加过滤器链中 11 个位置的第 7 位; 触发过滤器:'SecurityContextHolderAwareRequestFilter' 2019-10-11 02:22:33.659 调试 39216 --- [nio-8080-exec-2] os.security.web.FilterChainProxy:/admin/getCutOffDays at 附加过滤器链中的 11 位中的第 8 位;发射过滤器: 'AnonymousAuthenticationFilter' 2019-10-11 02:22:33.660 调试 39216 --- [nio-8080-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter :使用匿名令牌填充 SecurityContextHolder: 'org.springframework.security.authentication.AnonymousAuthenticationToken@25109219: 委托人:anonymousUser;凭证:[受保护];已认证: 真的;细节: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: 远程IP地址:0:0:0:0:0:0:0:1;会话ID:空;的确 权威机构:ROLE_ANONYMOUS' 2019-10-11 02:22:33.661 DEBUG 39216 --- [nio-8080-exec-2] os.security.web.FilterChainProxy : /admin/getCutOffDays 位于附加过滤器链中第 11 位的第 9 位; 触发过滤器:'SessionManagementFilter' 2019-10-11 02:22:33.662 DEBUG 39216 --- [nio-8080-exec-2] os.security.web.FilterChainProxy : /admin/getCutOffDays 位于附加过滤器链中第 11 位的第 10 位; 触发过滤器:'ExceptionTranslationFilter' 2019-10-11 02:22:33.671 调试 39216 --- [nio-8080-exec-2] os.security.web.FilterChainProxy : /admin/getCutOffDays 在附加过滤器中的第 11 位,共 11 位 链;触发过滤器:'FilterSecurityInterceptor' 2019-10-11 02:22:33.673 调试 39216 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher :检查请求的匹配: '/admin/getCutOffDays';针对 '/login' 2019-10-11 02:22:33.674 DEBUG 39216 --- [nio-8080-exec-2] os.s.w.a.i.FilterSecurityInterceptor : 安全对象:FilterInvocation:URL:/admin/getCutOffDays; 属性:[已验证] 2019-10-11 02:22:33.674 DEBUG 39216 --- [nio-8080-exec-2] os.s.w.a.i.FilterSecurityInterceptor : 之前认证: org.springframework.security.authentication.AnonymousAuthenticationToken@25109219: 委托人:anonymousUser;凭证:[受保护];已认证: 真的;细节: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: 远程IP地址:0:0:0:0:0:0:0:1;会话ID:空;的确 权威机构:ROLE_ANONYMOUS 2019-10-11 02:22:33.688 DEBUG 39216 --- [nio-8080-exec-2] os.s.access.vote.AffirmativeBased :选民: org.springframework.security.web.access.expression.WebExpressionVoter@74f28afc, 返回:-1 2019-10-11 02:22:33.696 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.w.a.ExceptionTranslationFilter :访问被拒绝(用户是 匿名的);重定向到身份验证入口点

org.springframework.security.access.AccessDeniedException: 访问是 拒绝 在 org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) ~[spring-security-core-5.0.8.RELEASE.jar!/:5.0.8.RELEASE] 在 org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233) ~[spring-security-core-5.0.8.RELEASE.jar!/:5.0.8.RELEASE] 在 org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:124) ~[spring-security-web-5.0.8.RELEASE.jar!/:5.0.8.RELEASE] 在 org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) ~[spring-security-web-5.0.8.RELEASE.jar!/:5.0.8.RELEASE]

我希望我对 /admin/getCutOffDays 的请求应该检索数据,因为它在之前的登录请求中已通过身份验证。

下面是我的控制器。

@RequestMapping("/admin")
@RestController
public class AdminController 
    @PreAuthorize("hasAuthority('ADMIN')")
    @GetMapping("/getCutOffDays")
    public long getCutOffDays() 
        return adminService.getSequenceId(CUT_OFF_DAYS);
    

谁能帮我弄清楚我在这里缺少什么。

【问题讨论】:

您好,在 URL 中传递密码不是一个好主意。我会改用 POST,以便密码在请求正文中发送。您能否详细说明您是如何实现 LDAP 身份验证的? 当您将会话策略标记为 SessionCreationPolicy.STATELESS。 Spring 不会为下一个请求保存会话。您需要在下一个请求中传递 JWT 令牌之类的令牌并创建 SecurityContextRepository 以保存会话。 如需更多了解,请使用dzone.com/articles/…了解如何实现JWT。 【参考方案1】:

要正确回答您的问题,我需要您提供更多意见。我会试着做一些假设来回答这个问题。

我假设您正在验证您的应用程序,然后进行 REST 调用。首先(正如答案中已经提到的),您有一个 STATELESS 会话策略。这意味着会话将无法用于后续的 Http 请求。

您可以通过以下方式处理它:

    实施基于令牌(透明/不透明)的身份验证。 实施基本身份验证。

在这两种情况下,对 REST API 的每个请求都需要一个授权标头(或 Cookie,取决于方法)。

【讨论】:

以上是关于Springboot和Spring Security中认证成功后如何授权Rest API调用的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot 优雅配置跨域多种方式及Spring Security跨域访问配置的坑

使用带有令牌的 spring 安全插件的一个好习惯

Spring OAuth2密码流一直失败

将 spring-boot-starter-security 添加到 Spring Boot 应用程序会导致错误 'entityManagerFactory' 或 'persistenceUnitNa

spring security

Keycloak Spring Boot 适配器和匿名资源