Spring Security:测试和理解 REST 身份验证
Posted
技术标签:
【中文标题】Spring Security:测试和理解 REST 身份验证【英文标题】:Spring Security : Testing & understanding REST authentication 【发布时间】:2015-05-30 08:55:18 【问题描述】:我正在开发 Spring-MVC,我正在使用 Spring-Security 登录/注销和会话管理。使用时效果很好 普通浏览器。我正在尝试为 申请,我参考了Answer on SO。我做了一些 修改,以便我可以在浏览器中和通过 REST 使用它 也。
现在,代码运行得很好,但我不知道为什么,而且如果我 想通过 REST 对用户进行身份验证,我该如何实现 代码?很遗憾,没有错误需要寻求解决方案。谁能告诉我这段代码,我应该通过 REST 发送哪些数据以进行身份验证并访问后续的@Secured 服务。 任何帮助都会很好。非常感谢。
安全应用程序上下文.xml:
<!-- Global Security settings -->
<security:global-method-security pre-post-annotations="enabled" />
<security:http pattern="/resources/**" security="none"/>
<security:http create-session="ifRequired" use-expressions="true" auto-config="false" disable-url-rewriting="true">
<security:form-login login-page="/login" default-target-url="/canvas/list" always-use-default-target="false" authentication-failure-url="/denied.jsp" />
<security:remember-me key="_spring_security_remember_me" user-service-ref="userDetailsService" token-validity-seconds="1209600" data-source-ref="dataSource"/>
<security:logout delete-cookies="JSESSIONID" invalidate-session="true" logout-url="/j_spring_security_logout"/>
<!--<security:intercept-url pattern="/**" requires-channel="https"/>-->
<security:port-mappings>
<security:port-mapping http="8080" https="8443"/>
</security:port-mappings>
<security:logout logout-url="/logout" logout-success-url="/" success-handler-ref="myLogoutHandler"/>
</security:http>
<!-- Rest authentication, don't edit, delete, add-->
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain-map path-type="ant">
<security:filter-chain filters="persistencefilter,authenticationfilter" pattern="/login"/>
<security:filter-chain filters="persistencefilter,logoutfilter" pattern="/logout"/>
<security:filter-chain pattern="/rest/**" filters="persistencefilter,restfilter" />
</security:filter-chain-map>
</bean>
<bean id="persistencefilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>
<bean id="authenticationfilter" class="com.journaldev.spring.utility.AuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="myAuthSuccessHandler"/>
<property name="passwordParameter" value="pass"/>
<property name="usernameParameter" value="user"/>
<property name="postOnly" value="false"/>
</bean>
<bean id="myAuthSuccessHandler" class="com.journaldev.spring.utility.AuthenticationSuccessHandler"/>
<bean id="myLogoutHandler" class="com.journaldev.spring.utility.MyLogoutHandler"/>
<bean id="logoutfilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg index="0" value="/"/>
<constructor-arg index="1">
<list>
<bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
<bean id="myLogoutHandler" class="com.journaldev.spring.utility.MyLogoutHandler"/>
</list>
</constructor-arg>
</bean>
<bean id="httpRequestAccessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false"/>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
</list>
</property>
</bean>
<bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter"/>
<bean id="restfilter" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<property name="securityMetadataSource">
<security:filter-invocation-definition-source>
<security:intercept-url pattern="/rest/**" access="ROLE_USER"/>
</security:filter-invocation-definition-source>
</property>
</bean>
<!-- Rest authentication ends here-->
<!-- queries to be run on data -->
<beans:bean id="rememberMeAuthenticationProvider" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
<beans:property name="key" value="_spring_security_remember_me" />
<beans:property name="tokenRepository" ref="jdbcTokenRepository"/>
<beans:property name="userDetailsService" ref="LoginServiceImpl"/>
</beans:bean>
<!--Database management for remember-me -->
<beans:bean id="jdbcTokenRepository"
class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
<beans:property name="createTableOnStartup" value="false"/>
<beans:property name="dataSource" ref="dataSource" />
</beans:bean>
<!-- Remember me ends here -->
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref="LoginServiceImpl">
<security:password-encoder ref="encoder"/>
</security:authentication-provider>
</security:authentication-manager>
<beans:bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="11" />
</beans:bean>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="LoginServiceImpl"/>
<beans:property name="passwordEncoder" ref="encoder"/>
</beans:bean>
身份验证过滤器:
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter
@Override
protected boolean requiresAuthentication(HttpServletRequest request,HttpServletResponse response)
return (StringUtils.hasText(obtainUsername(request)) && StringUtils.hasText(obtainPassword(request)));
@Override
protected void successfulAuthentication(HttpServletRequest request,HttpServletResponse response,
FilterChain chain, Authentication authResult) throws IOException, ServletException
System.out.println("Successfule authenticaiton");
super.successfulAuthentication(request,response,chain,authResult);
chain.doFilter(request,response);
AuthenticationSuccessHandler :
public class AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler
@PostConstruct
public void afterPropertiesSet()
setRedirectStrategy(new NoRedirectStrategy());
protected class NoRedirectStrategy implements RedirectStrategy
@Override
public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException
// Checking redirection issues
MyLogoutHandler :
public class MyLogoutHandler implements LogoutHandler
@Override
public void logout(HttpServletRequest request,HttpServletResponse response,Authentication authentication)
登录服务实现:
@Transactional
@Service("userDetailsService")
public class LoginServiceImpl implements UserDetailsService
@Autowired private PersonDAO personDAO;
@Autowired private Assembler assembler;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException,DataAccessException
Person person = personDAO.findPersonByUsername(username.toLowerCase());
if(person == null) throw new UsernameNotFoundException("Wrong username or password");
return assembler.buildUserFromUserEntity(person);
public LoginServiceImpl()
我尝试如下使用 Curl,但每次都得到 302。我想要的是获取 cookie,以及不同的状态,具体取决于身份验证是否成功。
akshay@akshay-desktop:~/Downloads/idea136/bin$ curl -i -X POST -d j_username=email@email.de -d j_password=password -c /home/cookies.txt http://localhost:8080/j_spring_security_check
HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=7F7B5B7056E75C138E550C1B900FAB4B; Path=/; HttpOnly
Location: http://localhost:8080/canvas/list
Content-Length: 0
Date: Thu, 26 Mar 2015 15:33:21 GMT
akshay@akshay-desktop:~/Downloads/idea136/bin$ curl -i -X POST -d j_username=email@email.de -d j_password=password -c /home/cookies.txt http://localhost:8080/j_spring_security_check
HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=BB6ACF38F20FE962924103DF5F419A55; Path=/; HttpOnly
Location: http://localhost:8080/canvas/list
Content-Length: 0
Date: Thu, 26 Mar 2015 15:34:02 GMT
如果还有其他需要,请告诉我。非常感谢。
【问题讨论】:
我已通过 RestTemplate 进行身份验证,如果有人需要,请在此处发表评论,我将包含它。 【参考方案1】:在获取 302 并保存 cookie 后尝试此 Curl 命令
curl -i --header "Accept:application/json" -X GET -b /home/cookies.txt http://localhost:8080/
希望对你有帮助
【讨论】:
我试过这个命令,我得到了整个页面的内容,但我也得到了 200。 akshay@akshay-desktop:~/Downloads$ curl -i --header "Accept:application/ json" -X GET -b /home/akshay/cookies.txt localhost:8080 HTTP/1.1 200 OK 但是即使登录失败也一样。 你能粘贴两种情况下的输出吗? 在清除 cookie 文件后,我能够使用上述 curl 行访问安全数据。我只需要在 C++ 中使用这种方法。 酷。如果您还有其他问题,请告诉我们。【参考方案2】:您可以使用基于表单的登录来使用浏览器登录,并使用 http 基本身份验证来登录 REST 端点。对于 xml 配置,分别是 和 。
Spring documentation 相同
【讨论】:
您能否建议我在主帖的代码中进行必要的更正以实现这一目标。 Rest 真正用于自动传输数据,当您向计算机显示包括表单在内的 ui 元素并期望它们充其量填补其膨胀时【参考方案3】:@我们是博格?您是否收到针对 REST URL 的 http 401 质询? 在浏览器上尝试该 url 并验证您是否看到一个要求您输入登录 ID 和密码的弹出窗口?
如果您使用 java 客户端进行测试,您可以在客户端代码中设置基本身份验证标头,如下所示。
CredentialsProvider provider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("user1", "user1Pass");
provider.setCredentials(AuthScope.ANY, credentials);
HttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(provider).build();
HttpResponse response = client.execute(new HttpGet(URL_SECURED_BY_BASIC_AUTHENTICATION));
int statusCode = response.getStatusLine().getStatusCode();
assertThat(statusCode, equalTo(HttpStatus.SC_OK));
【讨论】:
我应该尝试哪个 URL?本地主机:8080/j_spring_security_check?如果那是 URL,那么我会被拒绝页面。我)还没有集成你的代码,我应该在哪个文件中做上面的代码? 我想通过 C++ 使用 REST 身份验证,所以最好不要使用您提供的 Java 代码,而是使用一些基于浏览器的 URL 或 curl。以上是关于Spring Security:测试和理解 REST 身份验证的主要内容,如果未能解决你的问题,请参考以下文章
Grails - grails-spring-security-rest - 无法从 application.yml 加载 jwt 机密
使用 Spring Security 进行 Spring Boot 单元测试
Spring Boot整合Spring Security总结