I am having trouble in setting up the security at AJAX Calls for my webapp which uses Spring Security in backend for authentication my requests.
I have certain endpoints in my app like the following and I am accessing them through my website and mobile apps(Andriod, iOS).
I have added the basic authentication now for all /api/* urls but this is fine in case of Mobile Apps Access but if I use the same in AJAX calls in my website then I need to pass the username and password as headers request mentioned like in this link, but I don't want to reveal the username and password to the outside world as anybody can see them by just doing the view-source on the JS File.
Now what I want is that any username and password with ROLE_API can access the /api/* urls. But when accessing those URLs from website how can I pass the username and password in such a secured way that no other URL could access the /api/* urls when I need to do any CRUD operation using AJAX request from my website.
Below is the code for my security-config.xml, AuthenticationManager and Controller.
spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:security="http://www.springframework.org/schema/security"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<security:http auto-config="false" use-expressions="true"
access-denied-page="/login" entry-point-ref="authenticationEntryPoint">
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/api/**" access="hasRole('ROLE_API')"/>
<security:intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/user/**" access="hasRole('ROLE_USER')"/>
<security:logout invalidate-session="true" logout-success-url="/login" logout-url="/logout" />
<security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER" />
<security:custom-filter ref="authenticationFilter" position="BASIC_AUTH_FILTER" />
</security:http>
<security:global-method-security secured-annotations="enabled" />
<bean id="authenticationFilter"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
p:authenticationManager-ref="customAuthenticationManager"
p:authenticationFailureHandler-ref="customAuthenticationFailureHandler"
p:authenticationSuccessHandler-ref="customAuthenticationSuccessHandler" />
<bean id="customAuthenticationManager"
class="com.lumin.mytalk.authentication.CustomAuthenticationManager" />
<bean id="customAuthenticationFailureHandler"
class="com.lumin.mytalk.authentication.AuthenticationFailureUrlHandler" />
<bean id="customAuthenticationSuccessHandler"
class="com.lumin.mytalk.authentication.AuthenticationUrlHandler" />
<bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"
p:loginFormUrl="/login" />
<security:authentication-manager />
</beans>
CustomAuthenticationManager.java
public class CustomAuthenticationManager implements AuthenticationManager {
protected static Logger logger = Logger
.getLogger(CustomAuthenticationManager.class.getName());
@Autowired
private UserService userService;
private Md5PasswordEncoder passwordEncoder = new Md5PasswordEncoder();
public Authentication authenticate(Authentication auth)
throws AuthenticationException {
System.out.println("Performing custom authentication");
// Init a database user object
User user = null;
if (auth != null && auth.getName() != null && !auth.getName().equals("")) {
try {
// Retrieve user details from database
String username = auth.getName();
user = userService.getByEmail(username);
} catch (Exception e) {
System.out.println("User does not exists!");
throw new BadCredentialsException("User does not exists!");
}
if(user.getIsSocialUser() != null && user.getIsSocialUser()){
return new UsernamePasswordAuthenticationToken(auth.getName(), null, getAuthorities(user.getRole()));
}else{
String pwd = (String) auth.getCredentials();
if (passwordEncoder.encodePassword(pwd, null).equals(
user.getPassword())) {
System.out.println("Correct Password!");
System.out.println("User details are good and ready to go");
return new UsernamePasswordAuthenticationToken(auth.getName(), auth.getCredentials(), getAuthorities(user.getRole()));
} else {
System.out.println("Incorrect Password");
throw new BadCredentialsException("Incorrect Password !");
}
}
} else {
System.out.println("Username and Password are required");
throw new BadCredentialsException("Username and Password are required !");
}
}
public Collection<GrantedAuthority> getAuthorities(String role) {
// Create a list of grants for this user
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(0);
// Check if this user has admin access
if (role.equals(USERROLE.ROLE_USER.name())) {
System.out.println("Grant ROLE_USER to this user");
authList.add(new SimpleGrantedAuthority(USERROLE.ROLE_USER.name()));
} else if (role.equals(USERROLE.ROLE_ADMIN.name())) {
System.out.println("Grant ROLE_ADMIN to this user");
authList.add(new SimpleGrantedAuthority(USERROLE.ROLE_ADMIN.name()));
}
// Return list of granted authorities
return authList;
}
}
Sample Rest Controller
@RestController
public class CategoryController extends AbstractGenericController{
@Autowired
private CategoryService categoryService;
@ResponseBody
@RequestMapping(value = Path.Url.API + Path.Url.CATEGORY + Path.OperationUrl.CREATE, method = RequestMethod.POST, produces = { "application/json", "application/xml" })
public Object create(@RequestBody Category category) {
Category isExisting = categoryService.getCategoryByName(category.getCategoryName());
if (isExisting == null) {
Long id = categoryService.createWithID(category);
return new Response(null, STATUS.SUCCESS, "Category created successfully. Id :" + id);
}else{
return new Response(null, STATUS.FAILURE, "Category already exist. Please use update category function.");
}
}
}