0

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.");
        }
    }
}
Community
  • 1
  • 1
Ankur Jain
  • 1,386
  • 3
  • 17
  • 27
  • What's your question? And why would the username and the password be stored in a JS file? It's different for each user, I hope. – a better oliver Dec 12 '14 at 11:27
  • @zeroflagL What I want is that username and password with ROLE_API can access the **/api/* urls**. But when accessing those URLs from website how can I pass the data to **/api/* urls** when I need to do any CRUD operation using same urls using AJAX request. – Ankur Jain Dec 12 '14 at 11:35
  • Your comment and update don't make things any clearer. Let's say there's a user john. Is it right that this user can have both `ROLE_USER`and `ROLE_API`? Then at some point the credentials have to be entered and those are sent to the server. Obviously you know how to do that, so I still don't see your problem. If you have only one user with the `ROLE_API`, then you are doing something wrong. That's essentially like having no authentication at all. – a better oliver Dec 12 '14 at 11:52

1 Answers1

0

The username and password can be base64encoded and passed in the Authentication header.

abhati
  • 309
  • 1
  • 6