Auth with interceptors

Lets control page access using Madvoc interceptors. If user is not yet authenticated, it will be redirected to the login page. After the successful login, user continues with the requested page.

AuthInterceptor

Interceptor is quite simple:

    public class AuthInterceptor extends ActionInterceptor {

        @Override
        public Object intercept(ActionRequest request) throws Exception {
            HttpServletRequest servletRequest = request.getHttpServletRequest();
            HttpSession session = servletRequest.getSession();
            if (AuthUtil.isSessionActive(session)) {
                return request.invoke();
            }
            servletRequest.setAttribute(
                "path", DispatcherUtil.getActionPath(servletRequest));
            return "chain:/%login%";
        }
    }

AuthUtil.isSessionActive checks if session is active by, for example, checking if some attribute is set in current http session. If user is still not logged in, we will save requested action path (url + query parameters) in request scope and chain to the login page. Chain result type is similar as redirect, except it happens inside of Madvoc.

Here we are referring login page via its alias ('%login%'). This is done so we do not have to hardcode the login page name inside java code.

Interceptors stacks

We will have two interceptors stacks: one for public pages, one for those that require authorization.

    // public interceptor stack (example)
    public class PublicInterceptorStack extends ActionInterceptorStack {

        public PublicInterceptorStack() {
            super(
                IdRequestInjectorInterceptor.class,
                PrepareInterceptor.class,
                ServletConfigInterceptor.class);
        }
    }
    // auth interceptor stack
    public class AuthInterceptorStack extends ActionInterceptorStack {

        public AuthInterceptorStack() {
            super(AuthInterceptor.class, PublicInterceptorStack.class);
        }
    }

One of above interceptor stacks has to be set as default. Lets assume that most of web application content requires authentication. Therefore, we will set defaultInterceptors parameter that belongs to component MadvocConfig. There are two ways to do this: one is in java, in AppWebApplication.init() method. Second way is setting as Madvoc parameter in madvoc.properties:

    madvocConfig.defaultInterceptors=\
        jodd.madvoc.interceptor.EchoInterceptor, \
        jodd.joy.madvoc.AuthInterceptorStack

Now, all actions with intercepted with default interceptors will be forbidden for public access.

Login action

Login page and action is the last part that we need to make. Login page is simple: besides login data login form must also send path info (previously set by interceptor):

    <j:form>
    <form action="login.post.do" method="post">
        <input type="hidden" name="path">
        name: <input type="text" name="user.name"/>
        <input type="submit"/>
    </form>
    </j:form>

Now, lets make the login action. Obviously, we need http session to start new user session if user authentication is ok. Although is easy to inject HttpSession instance in action using ScopeType.CONTEXT scope, we will create action that does not depend on servlets API.

    @MadvocAction
    @InterceptedBy(PublicInterceptorStack.class)
    public class LoginAction {

        @PetiteInject
        FooService fooService;

        @Action(alias="login")
        public void view() {
        }

        @In
        User user;

        @In
        String path;

        @Out(scope = ScopeType.SESSION, value = AuthUtil.AUTH_SESSION_NAME)
        User userSession;

        @Action(extension = "do")
        public String post() {
            user = fooService.findUser(user);
            if (user == null) {
                return "#";        // return BACK;
            }
            userSession = user;
            return "redirect:" + path;
        }
    }

Obviously, login page must be public - therefore the PublicInterceptorStack in line #2. View action in line #9 shows the login.jsp. It also defines login alias, used by interceptor (line #8). Second action (login.post.do) tries to authenticate user. If user data are bad, action will return back to login.jsp. If user exist, we prepare the user session object. It will be outjected to the session since annotation specifies so (line #18). At the and we instruct the redirection to the requested path.

One consequence: if user session exist and if user for some reason visits login page and enters wrong data, above code will terminate his session (because of outjection of userSession that is null). This can be fixed by injecting existing user session in the userSession field, using annotation: @InOut.