Spring Security:测试和理解REST身份验证

4
我正在使用Spring-Security为Spring-MVC进行登录/注销和会话管理。它在常规浏览器中使用时可以正常工作。我正在尝试为应用程序集成REST身份验证,我参考了SO上的答案。我进行了一些修改,以便我可以在浏览器和REST上使用它。

现在,代码运行良好,但是我不知道为什么。此外,如果我想通过REST验证用户,如何使用这段代码?遗憾的是,没有错误可以询问解决方案。

有谁能告诉我,在使用此代码时,我应该通过REST发送什么数据进行身份验证并访问随后的@Secured服务。任何帮助都将不胜感激。谢谢。

Security-application-context.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);

    }
}

认证成功处理程序:

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
        }
    }
}

我的注销处理程序:

public class MyLogoutHandler implements LogoutHandler {

     @Override
    public void logout(HttpServletRequest request,HttpServletResponse response,Authentication authentication){

     }
}

LoginServiceImpl:

@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实现了验证,如果有人需要,请在这里发表评论,我会将其包含在内。 - We are Borg
3个回答

2

在获取302并保存cookies后,尝试使用以下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 http://localhost:8080/ HTTP/1.1 200 OK - We are Borg
但是即使登录失败,结果仍然相同。 - We are Borg
你能在两种情况下都粘贴输出吗? - Naveen Garg
在我清除了cookies文件后,我能够通过上述curl命令行访问安全数据。然后我只需要在C++中使用这种方法。 - We are Borg
好的,请告知我们是否有进一步的问题。 - Naveen Garg
是的。如果您知道如何在使用QT发送请求到安全服务时将cookie中的值与请求集成,那将很好。 - We are Borg

1

@我们是博格?您是否收到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我应该尝试?是localhost:8080/j_spring_security_check吗?如果那就是URL,那么我会被拒绝。我还没有集成你的代码,在哪个文件中我应该执行上述代码? - We are Borg
我想在C++中使用REST身份验证,因此最好不要使用您提供的Java代码,而是使用一些基于浏览器的URL或curl。 - We are Borg

1
你可以使用基于表单的登录来在浏览器中登录,使用HTTP基本身份验证来登录REST端点。对于XML配置,分别是和。
请参考Spring文档了解更多信息。

你能给我建议一下主贴代码需要做哪些改动才能实现这个吗? - We are Borg
REST主要用于自动化数据传输,当您向计算机显示包括表单在内的UI元素并期望它们填写时,最多只是臃肿。 - Pomagranite

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接