使用后端Spring Security Java EE Server和前端iOS Objective-C客户端进行身份验证

使用后端Spring Security Java EE Server和前端iOS Objective-C客户端进行身份验证

问题描述:

我想验证在Java EE服务器后端的iOS应用上连接的用户. 在这里,您可以找到我的spring安全配置后端:

I want to authenticate a user connected on his iOS app on a Java EE server Back-end. Here, you can find my spring security configuration back-end :

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p" 
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/security
             http://www.springframework.org/schema/security/spring-security.xsd
             http://www.springframework.org/schema/util 
             http://www.springframework.org/schema/util/spring-util.xsd">

    <http auto-config="true" use-expressions="true">

        <intercept-url pattern="/home.mediator" access="permitAll" />
        <intercept-url pattern="/loginInvalid.security" access="permitAll" />
        <intercept-url pattern="/logout*" access="permitAll" />

        <intercept-url pattern="/user**" access="hasRole('ROLE_USER')"/>
        <intercept-url pattern="/admin**" access="hasRole('ROLE_ADMIN')"/>
        <intercept-url pattern="/*" access="isAuthenticated()" />

        <form-login login-page="/home.mediator" 
            default-target-url="/loginValid.security"
            authentication-failure-url="/loginInvalid.security" />

        <logout invalidate-session="true" 
            logout-success-url="/logoutSuccess.security" 
            logout-url="/logout.do"/>

    </http>

    <authentication-manager>
            <authentication-provider user-service-ref="customUserDetailsService">
            </authentication-provider>
    </authentication-manager>

</beans:beans>

这在后台Web应用程序的浏览器上完美运行. 但是问题是当我使用iOS Objective-C客户端访问后端Java EE服务器以进行这样的身份验证过程时:

This work perfectly on the browser on the back-office web application. But the problem is when i access my Back-end Java EE server with my iOS objective-C client for authentication process like this :

    NSString *login = self.loginTextField.text;
    NSString *password = self.passwordTextField.text;

    NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:8080/myJeeServer/j_spring_security_check"]];
    [request setHTTPMethod:@"POST"];
    NSString *post =[[NSString alloc] initWithFormat:@"j_username=%@&j_password=%@",login,password];
    [request setHTTPBody:[post dataUsingEncoding:NSUTF8StringEncoding]];

    NSHTTPURLResponse *urlResponse = nil;
    NSError *err;
    [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&err];
    NSLog(@"Request : %@",request);
    NSLog(@"Response : %d",urlResponse.statusCode);
    NSLog(@"Error : %d",err.code);

根据iOS客​​户端,每次HTTP响应状态代码为200时,我都无法检查身份验证是成功还是失败.也许这种方式是使用Spring安全后端和Objective-C客户端前端实施身份验证的错误方式,但是我在网络上找不到其他任何方式.有人可以帮我解决这个安全用例吗?使用Objective-C和Spring安全性对用户进行身份验证的最佳方法是什么?

I've got every time an HTTP Response status code to 200, i can't check if the authentication was an success or a failure according to the iOS client side. Maybe this way is the wrong way to implement an authentication with Spring security Back-end and Objective-C client Front-end but i did not found any others ways on the web. Can someone help me with this security use case please ? What is the best way to authenticate user with objective-C and Spring security ?

感谢您的回答.

亲爱的

我忘了把这个老话题的解决方案放在下面,所以对于面临相同情况的开发人员来说,下面是一个可能的解决方案:

I forgot to put the solution of this old topic so below one possible solution for developers facing same situation :

  • 在iOS客户端上通过HTTP进行登录的示例,后面带有Spring安全认证:

-(IBAction)connect:(UIButton *)发送器{

-(IBAction)connect:(UIButton *)sender {

        // Get login and password values
        NSString *login = self.loginTextField.text;
        NSString *password = self.passwordTextField.text;

        // Check if they are not empty before HTTP call
        if([login isEqualToString:@""] || [password isEqualToString:@""]) {
            self.loginResponse.text = @"Please select a login and a password before connect.";
            return;
        } else {
            self.userLogin = login;
            self.userPassword = password;
        }

        // Prepare request for login
        NSString *restRequest =[[NSString alloc] initWithFormat:@"%@/rest/%@",IP_SERVER,REST_LOGIN_OPERATION];
        NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:[NSURL URLWithString:restRequest]];
        [request setHTTPMethod:@"POST"];
        NSString *post =[[NSString alloc] initWithFormat:@"j_username=%@&j_password=%@&submit=Login",self.userLogin,self.userPassword];
        [request setHTTPBody:[post dataUsingEncoding:NSUTF8StringEncoding]];

        // start the animation if it's not already going
        [self.spinner startAnimating];

        dispatch_queue_t loginQ = dispatch_queue_create("endoServerLoginQueue", NULL);
        dispatch_async(loginQ, ^(){
            // call Web Service REST on EndoServer for login
            NSHTTPURLResponse *urlResponse = nil;
            NSError *err;
            NSData *urlData =[NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&err];

            // Log async call request
            NSLog(@"[%@ %@] Request for customer login : %@",NSStringFromClass([self class]), NSStringFromSelector(_cmd), request);

            // When we have the results, use main queue to display them
            dispatch_async(dispatch_get_main_queue(), ^{

                // Parse data
                NSString *str=[[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];

                // Log async call response
                NSLog(@"[%@ %@] Response status code for doctor login : %d",NSStringFromClass([self class]), NSStringFromSelector(_cmd), urlResponse.statusCode);
                NSLog(@"[%@ %@] Response body for doctor login : (doctor id = %@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), str);
                NSLog(@"[%@ %@] Error code for doctor login : %d",NSStringFromClass([self class]), NSStringFromSelector(_cmd), err.code);

                if(urlResponse.statusCode == 200) {

                    [self.logoutButton setEnabled:YES];

                    NSLog(@"[%@ %@] Connection Success for Customer login (%@) and Password (%@).",NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.userLogin,self.userPassword);


                    // Save login and password in user preferences
                    NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
                    [userPreferences setObject:self.userLogin forKey:USER_LOGIN_KEY];
                    [userPreferences setObject:self.userPassword forKey:USER_PASSWORD_KEY];
                    [userPreferences setInteger:[str integerValue] forKey:USER_ID_KEY];
                    [userPreferences synchronize];

                    // Go to Home screen view
                    str = [NSString stringWithFormat:@"Welcome %@ ! ",self.userLogin];
                    [self performSegueWithIdentifier:@"goToHomePage" sender:self];
                }

                if(err.code == -1004) {
                    str = @"Server is shutdown, please try later or contact administrator.";
                    NSLog(@"[%@ %@] Error message : %@",NSStringFromClass([self class]), NSStringFromSelector(_cmd), str);
                }

                self.loginResponse.text = str; // makes UIKit calls, so must be main thread
                [self.spinner stopAnimating]; // stop the animation
            });
        });

}

  • 带有自定义身份验证处理程序的spring安全配置示例:

  • Sample of spring security configuration with custom authentication handler:

.....

<context:annotation-config />
<context:component-scan base-package="com....." />

....

<!-- 
    Add Rest API access for mobile devices
 -->
<http pattern="/rest/**" entry-point-ref="authenticationEntryPoint" use-expressions="true">
    <intercept-url pattern="/rest/login" access="hasRole('ROLE_CUSTOMER')" />
    <intercept-url pattern="/rest/customer/**" access="hasRole('ROLE_CUSTOMER')"/>
    <intercept-url pattern="/rest/logout" access="isAuthenticated()" />
    <form-login login-processing-url="/rest/login"
                authentication-success-handler-ref="customAuthenticationSuccessHandler"
                authentication-failure-handler-ref="customAuthenticationFailureHandler"/>
    <logout success-handler-ref="customLogoutSuccessHandler" logout-url="/rest/logout" invalidate-session="true"/>
</http>


....

和处理程序实现的示例:

and sample of handler implementation :

@Component(value = "customAuthenticationSuccessHandler")
public class CustomAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    private static final Logger LOGGER = Logger.getLogger(CustomAuthenticationSuccessHandler.class);

    @Autowired
    private IUserRepository userRepository;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
            HttpServletResponse response, Authentication authentication)
                    throws IOException, ServletException {
        if (hasRoleCustomer(authentication)) {
            String username = authentication.getName();
            User user = userRepository.findByUsername(username);
            response.setStatus(HttpServletResponse.SC_OK);
            response.getWriter().print(user.getId());
            LOGGER.info("Authentication Success for customer : "+user.getUsername()+", with id : "+user.getId());
        } else {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().print("Access Denied, please contact the administrator.");
            LOGGER.info("Access denied for customer : "+authentication.getName());
        }
        response.getWriter().flush();
    }

其他必需的处理程序应根据您的业务需求以相同的方式实现.

Others required handler should be implemented in the same way according to your business requirements.