1
votes

We have an application which uses spring basic authentication for security. The application is not built on Spring MVC. We are using the polymer as front end and exposing services as rest based one.

The requirement is to implement a login form, on click of submit button need to invoke a javascript which should post username/password.

Is there a way to pass credentials to spring basic authentication servlet from javascript, so that it validates the request. We have implemented AuthenticationProvider authenticate to perform the validation.

Authentication java code

import org.springframework.security.authentication.*;
import org.springframework.security.core.*;
import java.util.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import com.lifesciencemeta.ls.User;
import com.lifesciencemeta.ls.LifeScienceServiceMaster;
import com.lifesciencemeta.ls.LifeScienceConstants;

public class SpringBasicAuthentication implements AuthenticationProvider {
    public LifeScienceServiceMaster lifeScienceService;
    @Context
    UriInfo lsUri;
    public LifeScienceServiceMaster getLifeScienceService() {
        return lifeScienceService;
    }
    public void setLifeScienceService(LifeScienceServiceMaster lifeScienceService) {
        this.lifeScienceService = lifeScienceService;
    }
    @Override
    public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
        String principal = (String) auth.getPrincipal();
        String credential = (String) auth.getCredentials();
        User u = lifeScienceService.authenticateUser(principal, credential);
        if (u == null)
            throw new BadCredentialsException(LifeScienceConstants.getMsg(“Auth Failed"));
        else {
            List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
            String role = u.getRole().getName();
            if(role == null) {
                throw new BadCredentialsException(LifeScienceConstants.getMsg(“Auth Failed"));
            }
            grantedAuths.add(new SimpleGrantedAuthority(role));
            UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
                    principal, credential, grantedAuths);

            result.setDetails(u);
            return result;
        }

    }
    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);

    }

}

Web.xml

http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> org.springframework.web.context.ContextLoaderListener contextConfigLocation /WEB-INF/context.xml springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy springSecurityFilterChain / Jersey REST Service com.sun.jersey.spi.container.servlet.ServletContainer com.sun.jersey.config.property.packages com.lifesciencemeta.ls.restService.Invoke com.sun.jersey.api.json.POJOMappingFeature true 1 Jersey REST Service /rest/

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:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
               http://www.springframework.org/schema/security
               http://www.springframework.org/schema/security/spring-security-3.1.xsd">
        <security:global-method-security secured-annotations="enabled" jsr250-annotations="enabled" pre-post-annotations="enabled" />
        <security:http>
              <security:intercept-url   pattern="/**"  access="ROLE_USER, ROLE_ADMIN"/>
             <security:http-basic />
        </security:http>
        <security:authentication-manager alias="authenticationManager">
            <security:authentication-provider ref="SpringBasicAuthentication" />
        </security:authentication-manager> 
        <bean id="SpringBasicAuthentication" 
            class="com.lifesciencemeta.ls.SpringBasicAuthentication" >
            <property name="lifeScienceService" ref="lsLifeScienceServiceImpl"/>
        </bean>
    </beans>
1
So you are using Spring Security, right? The endopoint is /login for such task.Nano
Thanks for suggestion !user7934931

1 Answers

0
votes

You can configure spring security to use basic authentication and then just send a request to any protected resource plus authorization header with basic authentication info.

@SpringBootApplication
public class So44459836Application {

    public static void main(String[] args) {
        SpringApplication.run(So44459836Application.class, args);
    }

    @EnableWebSecurity
    public static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
        @Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers("/public/**"); //for static resources
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.formLogin().disable() //disable integrated login form 
                    .httpBasic().and() // we want http basic authentication
                    .authorizeRequests().anyRequest().authenticated(); // all requests are protected
        }
    }

    @RestController
    @RequestMapping("/api/me")
    public static class MeController {

        @GetMapping
        public String me(Principal principal) {
            return principal.getName(); // just return current username. 
        }
    }
}

Sample form

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>App</title>
</head>
<body>
<div>
    <section> <strong>Login</strong></section>

    <div>
        <label for="username">Username</label>
        <input type="text" id="username">
    </div>
    <div>
        <label for="password">Password</label>
        <input type="password" id="password">
    </div>

    <input type="button" onclick="login();" value="Authenticate">
</div>
</body>
<script type="text/javascript">
    'use strict';

    function login() {
        var username = document.getElementById('username').value;
        var password = document.getElementById('password').value;

        var  xhr = new XMLHttpRequest();
        xhr.open('GET', '/api/me', false, username, password);
        xhr.onload = function() {
            if (xhr.status === 200) {
                alert('User\'s name is ' + xhr.responseText);
            }
            else {
                alert('Request failed.  Returned status of ' + xhr.status);
            }
        };
        xhr.send();
    }
</script>
</html>

Update

For older versions of spring-security JavaConfig is not available so might try this:

web.xml

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

    <display-name>Spring3 App</display-name>

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>spring-web</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>spring-web</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

spring-web-servlet.xml

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

    <mvc:resources mapping="/public/**" location="/public/"/>
</beans>

applicationContext.xml

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

    <context:component-scan base-package="com.stackoverflow.so44459836"/>
    <mvc:annotation-driven/>

    <sec:http auto-config="true">
        <sec:intercept-url pattern="/api/**" access="IS_AUTHENTICATED_FULLY" />
        <sec:http-basic/>
    </sec:http>

    <sec:authentication-manager>
        <sec:authentication-provider>
            <sec:user-service>
                <sec:user name="admin" password="admin" authorities="ROLE_ADMIN"/>
            </sec:user-service>
        </sec:authentication-provider>
    </sec:authentication-manager>
</beans>

Place sample controller from the example above to package specified in component-scan and the html file to webapp/public directory and you should be good.

PS. I have tried this sample with spring-webmvc:3.1.4.RELEASE and spring-security-{web,config}:3.1.7.RELEASE.