使用后端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.