Showing posts with label spring-security. Show all posts
Showing posts with label spring-security. Show all posts

02 March 2019

Spring security multiple authentication provider


In your WebSecurityConfigurerAdapter you will need to add more than auth

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

    auth.authenticationProvider(new MyFirstAuthenticationProvider(userRepository, bCryptPasswordEncoder()));

    auth.authenticationProvider(new MySecondAuthenticationProvider(userRepository, bCryptPasswordEncoder()));

}

Then Create MyFirstAuthenticationProvider and MySecondAuthenticationProvider like:

public class MyFirstAuthenticationProvider extends DaoAuthenticationProvider {

    public MyFirstAuthenticationProvider(UserRepository userRepository,
                                              BCryptPasswordEncoder bCryptPasswordEncoder) {

        super.setPasswordEncoder(bCryptPasswordEncoder);
        super.setUserDetailsService(......)
        );
    }

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


public class MyFirstAuthenticationToken extends UsernamePasswordAuthenticationToken {

    public MyFirstAuthenticationToken(UserEntity principal, Object credentials,
                                                   Collection<? extends GrantedAuthority> authorities) {
        super(principal, credentials, authorities);
    }
}
And the same for MySecondAuthenticationProvider.

You will need to use the authentication providers/token in the authenticaiton/authorization filters.


24 February 2019

Where to save user authentication token: cookies vs local storage

Here's a very good comparison:
https://stackoverflow.com/a/35347022/171950

The important part in the link above is that local storage approach is CSRF protected, but exposed to XSS.

see also:
https://docs.microsoft.com/en-us/aspnet/core/security/anti-request-forgery?view=aspnetcore-2.2

01 January 2019

OAuth 2.0 and OpenID Connect (OIDC) flows

4 flows for OAuth 2.0:
  • Code flow:
    • auth server sends auth authorization code then client uses the code to get the access token
    • fit for web apps (where the work is done over a front-channel) 
    • has refresh token
  • Implicit flow:
    • auth server sending the access token directly
    • fit where the client is a native app (mobile, desktop, etc)
    • doesn't have a refresh token, but some people work around it.
  • Resource owner credential flow:
    • the client app gets a token by sending the username/password of the resource owner to the auth server
    • fit for enterprise-trusted apps (like on-prem services, like on-prem JIRA server when user enter his LDAP credentials), or regular client apps that connect to the corresponding backend service. (like mobile front-end connects to the backend, so the resource owner enters his own username/password into the client app to get an access token and a refresh token)
    • has refresh token.
  • Client credential:
    • service-to-service flow, no human interaction.
OpenID Connect (OIDC) builds on top of OAuth 2.0 by adding extra staff like id_token and userInfo endpoint and it reuses the first two flows (code & implicit for server and native clients respectively)

15 January 2016

spring security java config for REST service

I am trying to setup spring security for REST service in spring.

All worked fine, but when I tried to do logout, I find that instead of session being invalidated; some redirect happens to /login url (which is not exists).

The solution is to tell the logout configuration to only send 200 stauts code and not to redirect to the login page (hence this is a rest app and there's no login page - the client can be mobile app, js client like angular, etc..)

Here's the configurations:

http
.httpBasic()
.and().authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and().logout().logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())

.and().csrf().disable(); // for development only

10 April 2015

About web security (1)

We usually implement web security with Roles and Permissions.

a Database Table for users and one for Roles which users belongs and one for Permission for the roles which the users a role should have.


[Users Table]    <- Many-to-one -> [Roles Table] <-  One-to-many -> [Permissions Table]

The above model is the basic, but we can add extra relation between the Users and the Permissions to allow direct permission on the table regarding the role in which the user belongs.


The Authentication does from the Users table.. the user is there and not disabled.

The Authorization is all about answering the question of "Is the user has access to this restricted object?"

How Authorization done?

In Web - where the restricted object is all about URL resources - the autorization is done as follows:

  • Every resource (a pattern for resources) in the system (should) have a list of permissions associated (usually only one) with it.
    Usually defined in the Permissions table as URL -> PERMISSION mapping. (or in XML as of spring-security)
    ex: '/editMyProfile.html' url mapped to CAN_EDIT_PROFILE permission.
  • When the user successfully login, a list of all permission is attached to him (either role permissions or user permission as discussed before)
  • Then when the user requests some URL, The system checks if the user has some attached permission that in the list of permissions that is associated with the url. If yes then the system allows the user to access the page, otherwise HTTP 403 is returned to the user.

How to draw the menu:

First you have your structure of menu (tree menu, list menu or whatever).
Initially, get all urls from Permissions table of the database and draw this permissions as following:

for (Permission p: permissions){
     if (userHavePermission(p.getPermissionName()){
           out.println(p.getUrl());
     }
}

the above is very simple code example.
Also you can consider nesting the menus by having a self-referential relationship in the Permissions table.










20 November 2014

Implementing Spring Security with JSF (Login + RedirectStrategy)

When implementing Spring Security in JSF application, many problems happens, and one of the most important one is the redirection in case of HTTP errors!

In this post, I'll take about 2 points, the first point is how to implement the Login page (you will find many resources illustrate this - for example this) but I'll extend it allowing the user to continue on the page he was coming from (aka. Saved Request URL), the second point and the more important one is handling HTTP errors in case of Ajax requests.

Point #1 Handle Login From:

Initially, you have your JSF application (in my case I use Primefaces, but shouldn't matter at all, because the solution is JSF related not primefaces-related)


You have your xhtml login page (login.xhtml) like this:


<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui">
<f:view>
    <h:form id="loginForm">
        <h:messages style="color: red;" />

        <table>
            <tr>
                <td><p:outputLabel for="usernameTxt" value="Username" /></td>
                <td><p:inputText id="usernameTxt" value="#{loginBean.username}" /></td>
            </tr>
            <tr>
                <td><p:outputLabel for="passwordTxt" value="Password" /></td>
                <td><p:password id="passwordTxt" value="#{loginBean.password}" /></td>
            </tr>
            <tr>
                <td colspan="2"><p:commandButton value="Login"
                        action="#{loginBean.login}" /></td>
            </tr>
        </table>
    </h:form>
</f:view>

</html>

Very simple!, regularly with spring security, we submit the form to /j_spring_security_check but we have different situation here, we have a command button which send POST request to the same URL of the page.

2 solutions here, I'll take about one of them (as appears in Macro-blog link above) which is using a reference for the authenticationManager from the bean action method:

first, you need to expose the authenticationManager in the security context configuration file:


    <authentication-manager alias="authenticationManager">
        <authentication-provider>
            <user-service>
                <user name="admin" password="admin" authorities="ROLE_ADMIN" />
            </user-service>
        </authentication-provider>
    </authentication-manager>

Note the use of "alias" attribute.

Then in you JSF Managed bean, you can inject it and use it as the following:

@Inject
private AuthenticationManager authenticationManager;

@Setter
@Getter
private String username;

@Setter
@Getter
private String password;

public LoginBean() {
}

public String login() {
    try {
        Authentication authentication = authenticationManager
                .authenticate(new UsernamePasswordAuthenticationToken(
                        this.username, this.password));

        SecurityContextHolder.getContext()
                .setAuthentication(authentication);

    } catch (AuthenticationException ex) {
        log.equals(ex.getMessage());
        Util.addMessage("Login Failed: " + ex.getMessage());
        return "";
    }

    return Util.getSavedUrl() + "?faces-redirect=true";
}

The code above is pretty clear, If fail (AuthenticationExcpetion thrown, show error message), else redirect to some URL.

The point here is some URL is critical, because if the user was trying to access resource "A", then spring security asked him to login first, then after login he should access resource "A" itself not a welcome screen.

This handled by spring security, but in our case, we need to ask Spring-security explicity to give us the "SavedURL" from the session. so the implmenetation of Util.getSavedUrl() is below:

public static String getSavedUrl() {
    HttpServletRequest request = ((HttpServletRequest) FacesContext
            .getCurrentInstance().getExternalContext().getRequest());

    SavedRequest savedRequest = new HttpSessionRequestCache().getRequest(
            request, (HttpServletResponse) FacesContext
                    .getCurrentInstance().getExternalContext()
                    .getResponse());

    if (savedRequest != null) {
        try {
            URL url = new URL(savedRequest.getRedirectUrl());
            return url.getFile().substring(
                    request.getContextPath().length());
        } catch (Exception e) {
            log.error(e.getMessage() + " Using default URL");
        }
    }
    return "admin/index.xhtml?faces-redirect=true"; // default page!
}

And that's all for the first point.

Point #2 Handle HTTP error codes for Ajax requests:

JSF (and jsf based frameworks) uses Ajax in many cases,  the PrimeFaces autocomplete is a good example.

Originally with regular HTTP requests (non-Ajax), when the user try to access a resource after session expired, spring security will send error code 302 to the browser, when browser sees the response code, he will do the redirect the Login page.


But in case of Ajax requests, the ajax client should handle this on his own.

In case of JSF, (I am not the JSF expert Guy :D ) JSF uses some internal protocol sending XML between the server and the client, and this XML is well defined in matter if the server wants the client to do redirect, the server doesn't send 302 for the client, instead, he sends a response like:

<?xml version="1.0" encoding="UTF-8"?>
<partial-response>
    <redirect url="URL to redirect to"></redirect>
</partial-response>

So, when the ajax caller (who send the JSF request), see this response, he will do redirect immediately.


So, to integrate this with Spring security we need to tell it override the cod that sends the response code and add the above xml payload just in case of Ajax requests.

To do this Spring security provide a redirect strategy (org.springframework.security.web.RedirectStrategy) that you can extends and hook the spring security at some points.

So, here's a sample implementation:


package testing.ss;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.util.UrlUtils;

/**
 * Based on code from DefaultDirectStrategy and
 * https://gist.github.com/banterCZ/5160269
 * 
 * @author mhewedy
 *
 */
public class JsfRedirectStrategy implements RedirectStrategy {

    private static final String FACES_REQUEST_HEADER = "faces-request";

    protected final Log logger = LogFactory.getLog(getClass());

    private boolean contextRelative;

    /**
     * Redirects the response to the supplied URL.
     * <p>
     * If <tt>contextRelative</tt> is set, the redirect value will be the value
     * after the request context path. Note that this will result in the loss of
     * protocol information (HTTP or HTTPS), so will cause problems if a
     * redirect is being performed to change to HTTPS, for example.
     */
    public void sendRedirect(HttpServletRequest request,
            HttpServletResponse response, String url) throws IOException {
        String redirectUrl = calculateRedirectUrl(request.getContextPath(), url);
        redirectUrl = response.encodeRedirectURL(redirectUrl);

        boolean ajaxRedirect = "partial/ajax".equals(request
                .getHeader(FACES_REQUEST_HEADER));
        if (ajaxRedirect) {

            String ajaxRedirectXml = createAjaxRedirectXml(redirectUrl);
            logger.debug("Ajax partial response to redirect: "
                    + ajaxRedirectXml);

            response.setContentType("text/xml");
            response.getWriter().write(ajaxRedirectXml);
        } else {
            logger.debug("Non-ajax redirecting to '" + redirectUrl + "'");
            response.sendRedirect(redirectUrl);
        }
    }

    private String calculateRedirectUrl(String contextPath, String url) {
        if (!UrlUtils.isAbsoluteUrl(url)) {
            if (contextRelative) {
                return url;
            } else {
                return contextPath + url;
            }
        }

        // Full URL, including http(s)://

        if (!contextRelative) {
            return url;
        }

        // Calculate the relative URL from the fully qualified URL, minus the
        // last
        // occurrence of the scheme and base context.
        url = url.substring(url.lastIndexOf("://") + 3); // strip off scheme
        url = url.substring(url.indexOf(contextPath) + contextPath.length());

        if (url.length() > 1 && url.charAt(0) == '/') {
            url = url.substring(1);
        }

        return url;
    }

    /**
     * If <tt>true</tt>, causes any redirection URLs to be calculated minus the
     * protocol and context path (defaults to <tt>false</tt>).
     */
    public void setContextRelative(boolean useRelativeContext) {
        this.contextRelative = useRelativeContext;
    }

    // from https://gist.github.com/banterCZ/5160269
    private String createAjaxRedirectXml(String redirectUrl) {
        return new StringBuilder()
                .append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
                .append("<partial-response><redirect url=\"")
                .append(redirectUrl)
                .append("\"></redirect></partial-response>").toString();
    }

}


Then you have to hock the LoginUrlAuthenticationEntryPoint, but because the RedirectStrategy is a final variable, you have to extend it like this:

package testing.ss;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

/**
 * Based on code from LoginUrlAuthenticationEntryPoint
 * 
 * @author mhewedy
 *
 */
// see http://forum.spring.io/forum/spring-projects/security/88829-is-it-possible-to-change-spring-security-3-redirects-from-full-urls-to-relative-urls
@SuppressWarnings("deprecation")
public class JsfLoginUrlAuthenticationEntryPoint extends
        LoginUrlAuthenticationEntryPoint {

    Log log = LogFactory.getLog(getClass());

    private RedirectStrategy redirectStrategy;

    public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
        this.redirectStrategy = redirectStrategy;
    }

    @Override
    public void commence(HttpServletRequest request,
            HttpServletResponse response, AuthenticationException authException)
            throws IOException, ServletException {

        String redirectUrl = null;

        if (isUseForward()) {

            if (isForceHttps() && "http".equals(request.getScheme())) {
                // First redirect the current request to HTTPS.
                // When that request is received, the forward to the login page
                // will be used.
                redirectUrl = buildHttpsRedirectUrlForRequest(request);
            }

            if (redirectUrl == null) {
                String loginForm = determineUrlToUseForThisRequest(request,
                        response, authException);

                log.debug("Server side forward to: " + loginForm);

                RequestDispatcher dispatcher = request
                        .getRequestDispatcher(loginForm);

                dispatcher.forward(request, response);

                return;
            }
        } else {
            // redirect to login page. Use https if forceHttps true

            redirectUrl = buildRedirectUrlToLoginPage(request, response,
                    authException);
        }
        redirectStrategy.sendRedirect(request, response, redirectUrl);
    }

}


Then go back and configure http tag to reference the Entry Point:

<http auto-config="true" use-expressions="true" entry-point-ref="authenticationEntryPoint">
    .....
</http>

<beans:bean id="authenticationEntryPoint"
    class="testing.ss.JsfLoginUrlAuthenticationEntryPoint" p:loginFormUrl="/login.xhtml"
    p:redirectStrategy-ref="redirectStrategy" />
    
<beans:bean id="redirectStrategy" class="testing.ss.JsfRedirectStrategy" />

And that's all.

The complete source code on github.