JavaRush /Java Blog /Random EN /JAAS - Introduction to Technology (Part 2)
Viacheslav
Level 3

JAAS - Introduction to Technology (Part 2)

Published in the Random EN group
Continuation of the first part of the article about JAAS. Let's figure out whether it is possible to use only annotations for JAAS, and what problems we will encounter. We will learn in this part which Servlet API tools allow us to make the code more universal. And let's summarize what we read.
JAAS - Introduction to Technology (Part 2) - 1

Continuation

In the first part of the review of JAAS technology (see " JAAS - Introduction to Technology (Part 1) ") we looked at the main use case for JAAS and the Servlet API. We saw that the Tomcat servlet container managed the security of our web application using the JAAS architecture. Having knowledge of the “auth-method” and “Security Realm”, the Tomcat container itself provided us with the necessary implementation of the authentication mechanism and itself provided us with the CallbackHandler, we just used it all in our login module. The only important thing to remember is that the browser saves the login and password data transmitted via BASIC authentication. Therefore, for each new scan using Chrome, you can press Ctrl+Shift+N to open a new window to work in incognito mode.
JAAS - Introduction to Technology (Part 2) - 2

Annotations

Configuration using XML has long been out of fashion. Therefore, it is important to say that starting with Servlet API version 3.0, we have the opportunity to set servlet settings using annotations, without using the web.xml deployment descriptor file. Let's see how security management will change if we use annotations. And is it possible to implement the above described approaches using annotations? The Servlet API specification and its section “ Annotations and pluggability ” will again help us understand annotations . This section says that a servlet declaration web.xmlcan be replaced by an annotation @WebServlet. Accordingly, our servlet will look like this:

@WebServlet(name="app", urlPatterns = "/secret")
public class App extends HttpServlet {
Next, let's look at the chapter " 13.3 Programmatic Security ". It says that we can also declare a Security Constraint through annotations:

@WebServlet(name="app", urlPatterns = "/secret")
@ServletSecurity(httpMethodConstraints = {
        @HttpMethodConstraint(value = "GET", rolesAllowed = "admin")
})
public class App extends HttpServlet {
Now web.xmlthere is only one block left in ours - login-config. The problem is that it just so happens that there is no way to easily and simply replace it. Due to the close connection between web application security settings and web server security settings, there is no simple and universal way to do this, even programmatically. This is one of the problems with authentication using JAAS and Servlet API. Speaking about the block login-config, it is worth understanding that it is a declarative description of Authentication Mechanisms, i.e. authentication mechanisms. There is still no simple universal way to replace it, because... processing web.xmlhappens deep inside the servlet containers. For example, in Tomcat you can look at the source ContextConfig.java . Therefore, even for the Tomcat servlet container there are several options and they are all different. For example, if we use the Embedded Tomcat servlet container (i.e. we raise a web server from code), then you can read about such options here: “ Embedded Tomcat with basic authentication via code ”. In addition, a general example of raising Embedde Tomcat can be seen in the Heroku PaaS platform guide: “ Create a Java Web Application Using Embedded Tomcat ”. If Tomcat is not used in Embedded mode, then for Tomcat you can use a commonly used approach - event listeners. In Tomcat this is " The LifeCycle Listener Component ". At the same time, it is important to understand that servlet containers (in our case Tomcat) may have their own class loaders and it will not be possible to simply take and use your classes. For Tomcat you need to understand the " Class Loader HOW-TO ". In another servlet container called Undertow, this can be achieved using " Servlet Extensions ". As you can see, some have provided more flexible mechanisms, while others have not. As you can see, there is no single option. They are all very different. Is it possible to somehow do something universal with only the Servlet API and JAAS? On the Internet you can find a proposal to use Servlet Filter to perform authentication without a block login-config. Let's finally consider this option. This will allow us to repeat how JAAS works.
JAAS - Introduction to Technology (Part 2) - 3

Auth Filter

So, our goal is to get rid of web.xmlthe file completely. If we get rid of it, then, unfortunately, we will no longer be able to use Security Constraint, because their processing can occur much earlier than the servlet filters are applied. This is the fee you have to pay for the “versatility” of using filters. Those. we will have to remove the annotation @ServletSecurity, and all the checks that we previously described in the security constraint will have to be performed programmatically. As you can see, this option also imposes many unpleasant restrictions on us. First of all, let's remove the annotation @ServletSecurityfrom the resource and the block login-configfrom the web.xml. Now, we ourselves will have to implement Basic authentication. Now let's add our filter:

@WebFilter("/*")
public class JaasFilter implements javax.servlet.Filter {
So far it seems simple. Let's write an initialization method:

@Override
public void init(FilterConfig filterConfig) throws ServletException {
	String jaas_conf = filterConfig.getServletContext().getRealPath("/WEB-INF/jaas.config");
	System.getProperties().setProperty("java.security.auth.login.config",jaas_conf);
}
As you can see, we are now forced to use basic JAAS tools to search for the jaas config file. This has a big drawback - if there are several applications, then one application can break the authentication of another. How you can generally set a Jaas Config file is described in detail in the JAAS documentation: " Appendix A: JAAS Settings in the java.security Security Properties File ". Next, we will describe the filtering method itself. Let's start by receiving an HTTP request:

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
	HttpServletRequest req = (HttpServletRequest) request;
	// Если в реквесте уже есть Principal - ничего не делаем
	if (req.getUserPrincipal() != null ) {
		chain.doFilter(request, response);
	}
Everything is simple here. If Principal is available, then authentication is successful. Next, you need to describe the actions of the filter when the user has not passed authentication, i.e. he hasn't been recognized yet. Previously we described the basic authentication method, BASIC. Because Since we are now writing the filter ourselves, we will have to figure out how BASIC authentication actually works. You can use " MDN web docs: HTTP authorization ". And also " How Does HTTP Authentication Work? " From the description of how Basic authentication works, it is clear that if the server wants to perform BASIC authentication, and the user has not provided data, then the server sends a special header "WWW-Authenticate" and error code 401. Let's create an internal method for this:

private void requestNewAuthInResponse(ServletResponse response) throws IOException {
	HttpServletResponse resp = (HttpServletResponse) response;
	String value = "Basic realm=\"JaasLogin\"";
	resp.setHeader("WWW-Authenticate", value);
	resp.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
Now let's use this method and add doFilterthe following block of code to the method:

// Получаем security Header. А если его нет - запрашиваем
String secHeader = req.getHeader("authorization");
if (secHeader == null) {
	requestNewAuthInResponse(response);
}
Now let's add a branch for if, which will be executed when the header has already been transmitted:

// Проверяем аутентификацию
else {
	String authorization = secHeader.replace("Basic ", "");
	Base64.Decoder decoder = java.util.Base64.getDecoder();
	authorization = new String(decoder.decode(authorization));
	String[] loginData = authorization.split(":");
	try {
		if (loginData.length == 2) {
			req.login(loginData[0], loginData[1]);
			chain.doFilter(request, response);
		} else {
			requestNewAuthInResponse(response);
		}
	} catch (ServletException e) {
		requestNewAuthInResponse(response);
	}
}
You can add authorization to this code, for example: req.isUserInRole("admin") So we have done authentication with you using a filter. On the one hand, there is a lot of code and manual processing. On the other hand, there is versatility and server independence.
JAAS - Introduction to Technology (Part 2) - 4

Conclusion

Now we have reached the end of this review. I hope it will now become a little clearer what JAAS is, what a Subject is and what Principals are. The words Security Realm and Login Config will no longer raise questions. Additionally, we now know how to use JAAS and the Servlet API together. In addition, we learned about bottlenecks where annotations in the Servlet API will not save us. Of course, this is not all. For example, authentication in Java may not only be BASIC. You can see about other types in the Servlet API specification in the section " 13.6 Authentication ". It is difficult to find any detailed information on JAAS on the Internet. But I can recommend this material: I hope the information from this review will be useful and understandable. #Viacheslav
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION