Skip to content

Commit 75c1e48

Browse files
committed
Added sample for Java EE Security taking advantage of CDI 2
1 parent 55a1d37 commit 75c1e48

10 files changed

Lines changed: 522 additions & 0 deletions

File tree

security/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Java EE 8 Samples: Security 1.0#
2+
3+
The [JSR 375](https://jcp.org/en/jsr/detail?id=375) specifies the first version of the Java EE Security API - Java EE Security 1.0.
4+
5+
## Samples ##
6+
7+
- dynamic-rememberme
8+
9+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
3+
4+
<parent>
5+
<groupId>org.javaee8</groupId>
6+
<artifactId>security</artifactId>
7+
<version>1.0-SNAPSHOT</version>
8+
</parent>
9+
10+
<artifactId>dynamic-rememberme</artifactId>
11+
<packaging>war</packaging>
12+
<name>Java EE 8 Samples: Security - Dynamic Remember-me</name>
13+
14+
</project>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package org.javaee8.security.dynamic.rememberme;
2+
3+
import static java.util.stream.Collectors.toSet;
4+
5+
import java.util.logging.Logger;
6+
7+
import javax.annotation.Priority;
8+
import javax.enterprise.context.ApplicationScoped;
9+
import javax.enterprise.inject.Alternative;
10+
import javax.enterprise.inject.Produces;
11+
import javax.enterprise.inject.spi.Bean;
12+
import javax.enterprise.inject.spi.BeanManager;
13+
import javax.enterprise.inject.spi.InterceptionFactory;
14+
import javax.security.enterprise.authentication.mechanism.http.BasicAuthenticationMechanismDefinition;
15+
import javax.security.enterprise.authentication.mechanism.http.HttpAuthenticationMechanism;
16+
17+
import org.javaee8.security.dynamic.rememberme.util.HttpAuthenticationMechanismWrapper;
18+
import org.javaee8.security.dynamic.rememberme.util.RememberMeAnnotationLiteral;
19+
20+
// Configure a basic authentication mechanism.
21+
// In this sample we'll dynamically apply the @RememberMe annotation to this.
22+
@BasicAuthenticationMechanismDefinition(
23+
realmName="foo"
24+
)
25+
26+
@Alternative
27+
@Priority(500)
28+
@ApplicationScoped
29+
public class ApplicationInit {
30+
31+
private static final Logger logger = Logger.getLogger(ApplicationInit.class.getName());
32+
33+
@Produces
34+
public HttpAuthenticationMechanism produce(InterceptionFactory<HttpAuthenticationMechanismWrapper> interceptionFactory, BeanManager beanManager) {
35+
36+
logger.info("Producing wrapped and dynamic proxied mechanism");
37+
38+
// Get a reference (instance) to the HttpAuthenticationMechanism that would have been
39+
// used had we not provided an alternative here.
40+
//
41+
// In this sample that is the mechanism
42+
// the container puts into service following the @BasicAuthenticationMechanismDefinition
43+
// used above.
44+
HttpAuthenticationMechanism mechanism =
45+
createRef(
46+
beanManager.resolve(
47+
beanManager.getBeans(HttpAuthenticationMechanism.class)
48+
.stream()
49+
.filter(e -> !e.getBeanClass().equals(ApplicationInit.class))
50+
.collect(toSet())), beanManager);
51+
52+
// We're telling the InterceptionFactory here to dynamically add the @RememberMeAnnotation
53+
// annotation with the supplied values.
54+
interceptionFactory.configure().add(
55+
new RememberMeAnnotationLiteral(
56+
86400, "",
57+
false, "",
58+
true, "",
59+
"JREMEMBERMEID",
60+
true, ""
61+
)
62+
);
63+
64+
// This will create a proxy as configured above around an instance of
65+
// HttpAuthenticationMechanismWrapper that we provide.
66+
67+
// Note that we provide an extra wrapper since unfortunately createInterceptedInstance
68+
// says:
69+
// "If the provided instance is an internal container construct (such as client proxy), non-portable behavior results."
70+
return interceptionFactory.createInterceptedInstance(
71+
new HttpAuthenticationMechanismWrapper(mechanism));
72+
}
73+
74+
HttpAuthenticationMechanism createRef(Bean<?> bean, BeanManager beanManager) {
75+
return (HttpAuthenticationMechanism)
76+
beanManager.getReference(
77+
bean,
78+
HttpAuthenticationMechanism.class,
79+
beanManager.createCreationalContext(bean));
80+
}
81+
82+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package org.javaee8.security.dynamic.rememberme;
2+
3+
import java.io.IOException;
4+
5+
import javax.annotation.security.DeclareRoles;
6+
import javax.servlet.ServletException;
7+
import javax.servlet.annotation.HttpConstraint;
8+
import javax.servlet.annotation.ServletSecurity;
9+
import javax.servlet.annotation.WebServlet;
10+
import javax.servlet.http.HttpServlet;
11+
import javax.servlet.http.HttpServletRequest;
12+
import javax.servlet.http.HttpServletResponse;
13+
14+
@DeclareRoles({ "architect", "admin" })
15+
@WebServlet("/servlet")
16+
@ServletSecurity(@HttpConstraint(rolesAllowed = "architect"))
17+
public class Servlet extends HttpServlet {
18+
19+
private static final long serialVersionUID = 1L;
20+
21+
@Override
22+
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
23+
24+
String webName = null;
25+
if (request.getUserPrincipal() != null) {
26+
webName = request.getUserPrincipal().getName();
27+
}
28+
29+
response.getWriter().write("web username: " + webName + "\n");
30+
response.getWriter().write("web user has role \"architect\": " + request.isUserInRole("architect") + "\n");
31+
32+
}
33+
34+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.javaee8.security.dynamic.rememberme;
2+
3+
import static java.util.Arrays.asList;
4+
import static javax.security.enterprise.identitystore.CredentialValidationResult.INVALID_RESULT;
5+
6+
import java.util.HashSet;
7+
8+
import javax.enterprise.context.RequestScoped;
9+
import javax.security.enterprise.credential.UsernamePasswordCredential;
10+
import javax.security.enterprise.identitystore.CredentialValidationResult;
11+
import javax.security.enterprise.identitystore.IdentityStore;
12+
13+
@RequestScoped
14+
public class TestIdentityStore implements IdentityStore {
15+
16+
public CredentialValidationResult validate(UsernamePasswordCredential credential) {
17+
18+
if (!(credential.getCaller().equals("test") && credential.getPassword().compareTo("pass"))) {
19+
return INVALID_RESULT;
20+
}
21+
22+
return new CredentialValidationResult("test", new HashSet<>(asList("architect", "admin")));
23+
}
24+
25+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.javaee8.security.dynamic.rememberme;
2+
3+
4+
import static javax.security.enterprise.identitystore.CredentialValidationResult.INVALID_RESULT;
5+
6+
import java.util.Map;
7+
import java.util.Set;
8+
import java.util.UUID;
9+
import java.util.concurrent.ConcurrentHashMap;
10+
11+
import javax.enterprise.context.ApplicationScoped;
12+
import javax.security.enterprise.CallerPrincipal;
13+
import javax.security.enterprise.credential.RememberMeCredential;
14+
import javax.security.enterprise.identitystore.CredentialValidationResult;
15+
import javax.security.enterprise.identitystore.RememberMeIdentityStore;
16+
17+
@ApplicationScoped
18+
public class TestRememberMeIdentityStore implements RememberMeIdentityStore {
19+
20+
private Map<String, CredentialValidationResult> loginTokens = new ConcurrentHashMap<>();
21+
22+
@Override
23+
public CredentialValidationResult validate(RememberMeCredential credential) {
24+
if (!loginTokens.containsKey(credential.getToken())) {
25+
return INVALID_RESULT;
26+
}
27+
28+
return loginTokens.get(credential.getToken());
29+
}
30+
31+
@Override
32+
public String generateLoginToken(CallerPrincipal callerPrincipal, Set<String> groups) {
33+
String token = UUID.randomUUID().toString();
34+
loginTokens.put(token, new CredentialValidationResult(callerPrincipal, groups));
35+
36+
return token;
37+
}
38+
39+
@Override
40+
public void removeLoginToken(String token) {
41+
loginTokens.remove(token);
42+
}
43+
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.javaee8.security.dynamic.rememberme.util;
2+
3+
import javax.security.enterprise.AuthenticationException;
4+
import javax.security.enterprise.AuthenticationStatus;
5+
import javax.security.enterprise.authentication.mechanism.http.HttpAuthenticationMechanism;
6+
import javax.security.enterprise.authentication.mechanism.http.HttpMessageContext;
7+
import javax.servlet.http.HttpServletRequest;
8+
import javax.servlet.http.HttpServletResponse;
9+
10+
public class HttpAuthenticationMechanismWrapper implements HttpAuthenticationMechanism {
11+
12+
private HttpAuthenticationMechanism httpAuthenticationMechanism;
13+
14+
public HttpAuthenticationMechanismWrapper() {
15+
}
16+
17+
public HttpAuthenticationMechanismWrapper(HttpAuthenticationMechanism httpAuthenticationMechanism) {
18+
this.httpAuthenticationMechanism = httpAuthenticationMechanism;
19+
}
20+
21+
HttpAuthenticationMechanism getWrapped() {
22+
return httpAuthenticationMechanism;
23+
}
24+
25+
@Override
26+
public AuthenticationStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthenticationException {
27+
return getWrapped().validateRequest(request, response, httpMessageContext);
28+
}
29+
30+
@Override
31+
public AuthenticationStatus secureResponse(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthenticationException {
32+
return getWrapped().secureResponse(request, response, httpMessageContext);
33+
}
34+
35+
@Override
36+
public void cleanSubject(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) {
37+
getWrapped().cleanSubject(request, response, httpMessageContext);
38+
}
39+
40+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package org.javaee8.security.dynamic.rememberme.util;
2+
3+
import javax.enterprise.util.AnnotationLiteral;
4+
import javax.security.enterprise.authentication.mechanism.http.RememberMe;
5+
6+
@SuppressWarnings("all")
7+
public class RememberMeAnnotationLiteral extends AnnotationLiteral<RememberMe> implements RememberMe {
8+
9+
private static final long serialVersionUID = 1L;
10+
11+
int cookieMaxAgeSeconds;
12+
String cookieMaxAgeSecondsExpression;
13+
boolean cookieSecureOnly;
14+
String cookieSecureOnlyExpression;
15+
boolean cookieHttpOnly;
16+
String cookieHttpOnlyExpression;
17+
String cookieName;
18+
boolean isRememberMe;
19+
String isRememberMeExpression;
20+
21+
public RememberMeAnnotationLiteral(
22+
23+
int cookieMaxAgeSeconds,
24+
String cookieMaxAgeSecondsExpression,
25+
boolean cookieSecureOnly,
26+
String cookieSecureOnlyExpression,
27+
boolean cookieHttpOnly,
28+
String cookieHttpOnlyExpression,
29+
String cookieName,
30+
boolean isRememberMe,
31+
String isRememberMeExpression
32+
33+
) {
34+
35+
this.cookieMaxAgeSeconds = cookieMaxAgeSeconds;
36+
this.cookieMaxAgeSecondsExpression = cookieMaxAgeSecondsExpression;
37+
this.cookieSecureOnly = cookieSecureOnly;
38+
this.cookieSecureOnlyExpression = cookieSecureOnlyExpression;
39+
this.cookieHttpOnly = cookieHttpOnly;
40+
this.cookieHttpOnlyExpression = cookieHttpOnlyExpression;
41+
this.cookieName = cookieName;
42+
this.isRememberMe = isRememberMe;
43+
this.isRememberMeExpression = isRememberMeExpression;
44+
}
45+
46+
@Override
47+
public boolean cookieHttpOnly() {
48+
return cookieHttpOnly;
49+
}
50+
51+
@Override
52+
public String cookieHttpOnlyExpression() {
53+
return cookieHttpOnlyExpression;
54+
}
55+
56+
@Override
57+
public int cookieMaxAgeSeconds() {
58+
return cookieMaxAgeSeconds;
59+
}
60+
61+
@Override
62+
public String cookieMaxAgeSecondsExpression() {
63+
return cookieMaxAgeSecondsExpression;
64+
}
65+
66+
@Override
67+
public boolean cookieSecureOnly() {
68+
return cookieSecureOnly;
69+
}
70+
71+
@Override
72+
public String cookieSecureOnlyExpression() {
73+
return cookieSecureOnlyExpression;
74+
}
75+
76+
@Override
77+
public String cookieName() {
78+
return cookieName;
79+
}
80+
81+
public boolean isRememberMe() {
82+
return isRememberMe;
83+
}
84+
85+
@Override
86+
public String isRememberMeExpression() {
87+
return isRememberMeExpression;
88+
}
89+
}

0 commit comments

Comments
 (0)