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.
For reference, this page has 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’s 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 id="password" name="password" type="password" />
<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’s 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 log-in service that loads usernames from a Java properties file, whereas JDBCLoginService can read users from a JDBC data source.
|
Configuring Tomcat
<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