草庐IT

java - Android 设备针对 Spring Security Rest API 的身份验证

coder 2023-11-22 原文

我有一个带有 REST-API 的 SPRING 后端。它由用户名和密码保护。当我首先用我的电脑浏览器打开它时,会显示登录屏幕,在我添加凭据后,我可以流畅地访问 API。

当我通过 Android 应用程序尝试相同操作时,我每次都会看到登录屏幕。要在 Android 端进行身份验证,我使用默认情况下可访问的 REST API 请求。 内部 Android 应用程序浏览器是否不兼容存储 session cookie?每次创建新的 HTTP session 时。我使用 Volley 来处理请求

spring-security.xml

    <http auto-config="true" use-expressions="true">
    <intercept-url pattern="/api/user/login" access="permitAll" /> <!--IS_AUTHENTICATED_ANONYMOUSLY-->
    <intercept-url pattern="/admin/**" access="hasAnyRole('ROLE_ADMIN','ROLE_GROUP_LEADER')" />
    <intercept-url pattern="/api/**" access="hasAnyRole('ROLE_USER','ROLE_ADMIN','ROLE_GROUP_LEADER')" />

    <form-login 
        login-page="/login" 
        default-target-url="/admin" 
        authentication-failure-url="/login?error" 
        username-parameter="username"
        password-parameter="password"    
    />
    <access-denied-handler error-page="/403" />
    <logout logout-success-url="/login?logout" />
</http>


<authentication-manager alias="authManager">
   <authentication-provider >

        <password-encoder ref="encoder" />

        <jdbc-user-service data-source-ref="dataSource"
      users-by-username-query=
        "select username,password, enabled from user where username=?"
      authorities-by-username-query=
        "select username, name as role from role r,user u where u.role_id = r.id and username =? " />
    </authentication-provider>
</authentication-manager>

来自rest api的 Controller 中的代码

    UserDetails userDetails = userDetailsSvc.loadUserByUsername(user.getUsername());

    UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userDetails, "123",userDetails.getAuthorities());

    Authentication authentication = authManager.authenticate(token);

    log.debug("Logging in with [{}]", authentication.getPrincipal());
    SecurityContext securityContext = SecurityContextHolder.getContext();
    securityContext.setAuthentication(authentication);
    HttpSession session = request.getSession(true);
    session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);

Spring 安全调试

************************************************************

Request received for POST '/api/user/create':

org.apache.catalina.connector.RequestFacade@36dc8ced

servletPath:/api/user/create
pathInfo:null
headers: 
if-modified-since: Sat, 03 Jan 2015 12:35:50 GMT+00:00
content-type: application/json; charset=utf-8
user-agent: Dalvik/1.6.0 (Linux; U; Android 4.0.4; GT-P7100 Build/IMM76D)
host: 192.168.178.36:8088
connection: Keep-Alive
accept-encoding: gzip
content-length: 124


Security filter chain: [
  SecurityContextPersistenceFilter
  WebAsyncManagerIntegrationFilter
  LogoutFilter
  UsernamePasswordAuthenticationFilter
  BasicAuthenticationFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  FilterSecurityInterceptor
]


************************************************************


2015-01-03 13:53:46 INFO  Spring Security Debugger:39 - 

************************************************************

New HTTP session created: A430DD754F7F6E466D07B10D1DDCCEF7

Call stack: 

    at org.springframework.security.web.debug.Logger.info(Logger.java:29)
    at org.springframework.security.web.debug.DebugRequestWrapper.getSession(DebugFilter.java:144)
    at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:238)
    at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:238)
    at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:238)
    at org.springframework.security.web.savedrequest.HttpSessionRequestCache.saveRequest(HttpSessionRequestCache.java:40)
    at org.springframework.security.web.access.ExceptionTranslationFilter.sendStartAuthentication(ExceptionTranslationFilter.java:184)
    at org.springframework.security.web.access.ExceptionTranslationFilter.handleSpringSecurityException(ExceptionTranslationFilter.java:168)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:131)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.security.web.debug.DebugFilter.invokeWithWrappedRequest(DebugFilter.java:70)
    at org.springframework.security.web.debug.DebugFilter.doFilter(DebugFilter.java:59)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:503)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:136)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:74)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1015)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:652)
    at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1575)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1533)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:722)


************************************************************


2015-01-03 13:53:46 INFO  Spring Security Debugger:39 - 

************************************************************

Request received for GET '/login':

org.apache.catalina.connector.RequestFacade@36dc8ced

servletPath:/login
pathInfo:null
headers: 
if-modified-since: Sat, 03 Jan 2015 12:35:50 GMT+00:00
content-type: application/json; charset=utf-8
user-agent: Dalvik/1.6.0 (Linux; U; Android 4.0.4; GT-P7100 Build/IMM76D)
host: 192.168.178.36:8088
connection: Keep-Alive
accept-encoding: gzip


Security filter chain: [
  SecurityContextPersistenceFilter
  WebAsyncManagerIntegrationFilter
  LogoutFilter
  UsernamePasswordAuthenticationFilter
  BasicAuthenticationFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  FilterSecurityInterceptor
]


************************************************************


MonitorFilter::WARNING: the monitor filter must be the first filter in the chain.

Volley 抽射

    CookieManager manager = new CookieManager();
    CookieHandler.setDefault( manager  );

    mQueue = Volley.newRequestQueue(context);

最佳答案

我怀疑问题可能是您正在实现自己的自定义身份验证 Controller 端点,而不是使用完整的 spring-security 链。

正常的过滤器链仍然会根据您的 xml 配置在所有请求上被调用,但是只需调用 Controller 中的 authenticate() 方法就可以做到这一点,而不是其他身份验证成功处理程序的东西,例如您实际上不会在使用临时 Controller 身份验证的响应中设置 cookie)

最简单的测试方法是直接 curl url,或使用 postman (rest api 的 chrome 插件)之类的东西来测试 api 身份验证端点,看看是否在响应中设置了任何 cookeis。

如果您可以控制服务器端代码(例如,您可以更改它而不仅仅是在 Android 应用程序上工作),这里有一些想法:

  • 我会避免自定义身份验证端点并尝试手动处理安全问题 - Spring Security 非常擅长这些问题。

  • 假设您不想使用 oauth 以及 API 解决方案的复杂性,那么请查看 Google's recommended approach for securing APIs for mobile apps .它类似于您采用的方法,但对于登录,您只需在 android 应用程序中嵌入一个 web View ,并使用它让用户直接登录标准的 spring 安全表单(因此可以利用 spring-security 默认行为,例如cookie 等),然后在应用程序中,您只需从网络响应中获取一个用户 token ,然后存储它——然后可以在所有 API 请求中将其用作请求 header (因此用户不必继续登录移动应用程序)

我在这里写了一个方法的概述:

Securing your API for mobile access

并且在这里也整合了一个 spring security 实现。

Securing your mobile API with Spring Security

关于java - Android 设备针对 Spring Security Rest API 的身份验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27755249/

有关java - Android 设备针对 Spring Security Rest API 的身份验证的更多相关文章

  1. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  2. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  3. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  4. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  5. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  6. ruby-on-rails - 如何将验证与模型分开 - 2

    我有一些非常大的模型,我必须将它们迁移到最新版本的Rails。这些模型有相当多的验证(User有大约50个验证)。是否可以将所有这些验证移动到另一个文件中?说app/models/validations/user_validations.rb。如果可以,有人可以提供示例吗? 最佳答案 您可以为此使用关注点:#app/models/validations/user_validations.rbrequire'active_support/concern'moduleUserValidationsextendActiveSupport:

  7. ruby-on-rails - 跳过状态机方法的所有验证 - 2

    当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested

  8. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

  9. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  10. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

随机推荐