Configuring Servlet-Container Authentication
Servlet containers have different mechanisms to create the user Principal
object and run security checks when authentication is required for secured endpoints.
As a reference, we provide configuration examples of custom session-based authentication with two popular containers.
Session-Based Authentication
Configuring a Custom Servlet Filter
For customized authentication, you need to implement a custom HttpServletRequest
to wrap the default authentication using a WebFilter
.
The following example contains a customized Principal
object.
It is assumed that this principal is set as a Session
attribute at some point by the application.
public class CustomPrincipal implements Principal {
private final String name;
private final List<String> roles;
public CustomPrincipal(String name, String ...roles) {
this.name = name;
this.roles = Arrays.asList(roles);
}
public String getName() {
return name;
}
public boolean isUserInRole(String role) {
return roles.contains(role);
}
}
public class CustomHttpServletRequest extends HttpServletRequestWrapper {
public CustomHttpServletRequest(HttpServletRequest request) {
super(request);
}
@Override
public Principal getUserPrincipal() {
Principal myUser = (Principal) getSession().getAttribute("User");
return myUser != null ? myUser : super.getUserPrincipal();
}
@Override
public boolean isUserInRole(String role) {
return getUserPrincipal() instanceof CustomPrincipal
&& ((CustomPrincipal) getUserPrincipal()).isUserInRole(role)
|| super.isUserInRole(role);
}
}
@WebFilter("/connect")
public class CustomWebFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(new CustomHttpServletRequest((HttpServletRequest) request), response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void destroy() { }
}
Configuring a Client Log-in Form
Create a form to ask the user for a username/password pair and send it to the server side. The following snippet shows how to code a log-in web component.
import { html, LitElement } from 'lit';
import { customElement, query } from 'lit/decorators.js';
@customElement("my-login")
export class MyLogin extends LitElement {
@query('#username')
private username: any;
@query('#password')
private password: any;
render() {
return html`
<input id="username" name="username"></input>
<input id="password" name="password" type="password"></input>
<button @click="${this.login}">login</button>
`;
}
private login() {
fetch('/connect/login', {
method: 'POST',
body: `username=${this.username.value}&password=${this.password.value}`
})
.then(() => console.log("User Logged-in"));
}
}
Next, change the above filter to handle the request and set the appropriate principal object in the session.
@WebFilter("/connect")
public class CustomWebFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// Check username/password and set the `User` attribute in session
if ("foo".equals(request.getParameter("username")) &&
"abc123".equals(request.getParameter("password"))) {
((HttpServletRequest) request).getSession()
.setAttribute("User", new CustomPrincipal("foo"));
}
// wrap original request with our custom implementation
chain.doFilter(new CustomHttpServletRequest((HttpServletRequest) request), response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void destroy() { }
}
Configuring Servlet Container Authentication
The following sections show how to configure authentication in Jetty and Tomcat servers using the Basic HTTP authentication schema.
Note | Basic HTTP authentication is considered insecure. For public and production deployments, it is recommended to use form-based mechanisms or client-side certificates. Most servlet containers can be configured to use these. |
Configuring Jetty
test: password1,user
admin: password2,user,admin
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Set name="contextPath">/connect</Set>
<Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/my-app</Set>
<Get name="securityHandler">
<Set name="loginService">
<New class="org.eclipse.jetty.security.HashLoginService">
<Set name="name">my-app</Set>
<Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/jetty-users.properties</Set>
</New>
</Set>
</Get>
</Configure>
Note |
A 'realm' is a repository of user information.
HashLoginService is a simple log-in service that loads usernames from a Java properties file, whereas JDBCLoginService can read users from a JDBC data source.
|
Configuring Tomcat
.$CATALINA_HOME/conf/tomcat-users.xml
<tomcat-users>
<role rolename="admin" />
<role rolename="user" />
<user name="test" password="password1" roles="user" />
<user name="admin" password="password2" roles="user,admin" />
</tomcat-users>
<Context path="/connect">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase" />
</Context>
Note |
Change the realm implementation if you prefer to have a different user data source.
The provided UserDatabaseRealm can get users from a JDBC database.
|
A0A3832E-663B-47A6-B108-78F36B608B77