PerimeterX Java SDK
Latest stable version: v6.16.0
- Usage
- Basic Usage Example
- Advanced Usage Examples
- Configuration
- Logging and Troubleshooting
- Contributing
- Additional Information
Use jdk 1.7 or higher.
Make sure your JDK supports unlimited key length.
If the SDK is throwing Unlimited Strength Jurisdiction Policy assertion errors on startup, follow the instructions below:
- Download
JCEfor jdk17 or for jdk18. - Replace
local_policy.jarandUS_export_policy.jarin your$JAVA_HOME/jre/lib/security/with those you have downloaded. - Run your project again and the
Unlimited Strength Jurisdiction Policyerror should no appear.
- Add
perimeterx-sdktopom.xml:
<dependency>
<groupId>com.perimeterx</groupId>
<artifactId>perimeterx-sdk</artifactId>
<version>${VERSION}</version>
</dependency>- Add
perimeterx-sdkto yourbuild.gradle:
compile group: 'com.perimeterx', name: 'perimeterx-sdk', version: '${VERSION}'To upgrade to the latest Enforcer version, run:
mvn versions:display-dependency-updates
Open the project’s pom.xml and change the version number to the latest version.
Your Enforcer version is now upgraded to the latest enforcer version.
The PXContext on SDK v4.x has changed, following these changes, the implementation of PerimeterX SDK on the java filter must be changed accordingly.
PerimeterX SDK reports now if handled the response instead of reporting if request was verified (using ctx.isVerified()) instead, its PXContext expose the following methods: ctx.isHandledResponse().
isVerified() is deprecated and from now on, use isRequestLowScore()
isHandledResponse() will return true in the following cases
- Request is blocked and PerimeterX handled the response by rendering a block page (because score was high)
- Response was handled by first party mechanism (not score related).
- More information about First Party can be found in the configurations page
Following the instructions above, the filter should be changed according the the example below
// Verify the request
PXContext ctx = enforcer.pxVerify(req, new HttpServletResponseWrapper(resp);
// Notice that isVerified() changed to isHandledResponse()
if (ctx != null && ctx.isHandledResponse()) {
// Optional: check why response was handled
if (ctx.isFirstPartyRequest()) {
System.out.println("Incoming request was first party request");
}
if (!ctx.isRequestLowScore()) {
System.out.println("Request score was higher than threshold");
}
// Must return and not continue to filterChain.doFilter
return;
}
filterChain.doFilter(servletRequest, servletResponse);Once the filter is changed, follow the instructions above.
For more information, contact PerimeterX Support.
// Create configuration object
PXConfiguration pxConfiguration = new PXConfiguration.Builder()
.cookieKey(COOKIE_KEY)
.authToken(AUTH_TOKEN)
.appId(APP_ID)
.blockingScore(SCORE)
.moduleMode(ModuleMode.BLOCKING)
.build();
// Get instance
PerimeterX enforcer = new PerimeterX(pxConfiguration);
// Inside the request / Filter
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOExcption {
...
PXContext ctx = enforcer.pxVerify(req, new HttpServletResponseWrapper(resp);
if (ctx != null && !ctx.isHandledResponse()) {
// request should be blocked and BlockHandler was triggered on HttpServerResponseWrapper
}
...
}Please continue reading about the various configurations available on the sdk in the configurations page .
Users can access data enrichment information in two ways:
- Using context.getPxde() - Access the data enrichment payload directly in your Java code
- Using a custom header - Forward the data enrichment payload as a header to another server (e.g., your origin server)
MyVerificationHandler.java:
...
public class MyVerificationHandler implements VerificationHandler {
PXConfiguration pxConfig;
VerificationHandler defaultVerificationHandler;
public AutomationVerificationHandler(PXConfiguration pxConfig) throws PXException {
this.pxConfig = pxConfig;
PXClient pxClient = new PXHttpClient(pxConfig);
ActivityHandler activityHandler = new DefaultActivityHandler(pxClient, pxConfig);
this.defaultVerificationHandler = new DefaultVerificationHandler(pxConfig, activityHandler);
}
public boolean handleVerification(PXContext pxContext, HttpServletResponseWrapper httpServletResponseWrapper) throws PXException, IOException {
if (pxContext.isPxdeVerified()) {
JsonNode dataEnrichmentPayload = pxContext.getPxde();
<handle data enrichment payload here>
}
return defaultVerificationHandler.handleVerification(pxContext, httpServletResponseWrapper);
}
}Then, in your filter:
...
PXConfiguration config = new PXConfiguration.Builder()
...
.build();
PerimeterX enforcer = new PerimeterX(config);
enforcer.setVerificationHandler(new MyVerificationHandler(config));
...To forward the data enrichment payload to your backend/origin server, configure the header name. After pxVerify completes, the PXDE payload will be automatically added as a header to the request, which can then be forwarded:
PXConfiguration config = new PXConfiguration.Builder()
...
.pxDataEnrichmentHeaderName("X-PX-Data-Enrichment")
.build();
PerimeterX enforcer = new PerimeterX(config);
// In your filter:
PXContext ctx = enforcer.pxVerify(request, response);
// After pxVerify, the request now contains the data enrichment header
// and can be forwarded to your backend/origin server
// The header will be available as "X-PX-Data-Enrichment" in the request
filterChain.doFilter(request, response);With the customIsSensitive predicate you can force the request to be sensitive.
The input of the function is the same request that sent to the method pxVerify.
If the function throws exception, it is equivalent to returning false.
Implementing this configuration does NOT override other sensitive configurations, like sensitive_routes.
Note The request body can only be read once by default. If your function requires reading the body consider using RequestWrapper which caches the body. Send the wrapped request to
pxVerifyinstead of the native one.
In your filter:
...
PXConfiguration pxConfiguration = new PXConfiguration.Builder()
...
.customIsSensitiveRequest((req) -> req.getHeader("example-header") == "example-value")
.build();With the customParametersExtraction function you can add up to 10 custom parameters to be sent back to PerimeterX servers.
When set, the function is called before setting the payload on every request to PerimetrX servers.
The input of the function is the same request that sent to the method pxVerify.
If the function throws exception, it is equivalent to returning empty custom params.
Implementing this configuration overrides the deprecated configuration customParameterProvider.
Custom parameters support various types including strings, numbers, and booleans, allowing flexibility in the data sent to PerimeterX.
Note The request body can only be read once by default. If your function requires reading the body consider using RequestWrapper which caches the body. Send the wrapped request to
pxVerifyinstead of the native one.
In your filter:
...
PXConfiguration pxConfiguration = new PXConfiguration.Builder()
...
.customParametersExtraction((req) -> {
CustomParameters customParameters = new CustomParameters();
customParameters.setCustomParam1("example-value");
customParameters.setCustomParam2(req.getHeader("example-header"));
customParameters.setCustomParam3(123); // Numbers are supported
customParameters.setCustomParam4(true); // Booleans are supported
return customParameters;
})
.build();
...The SDK can extract user identifiers from JWT tokens in cookies or headers to enhance Account Defender capabilities. This allows PerimeterX to correlate user activity across sessions and improve detection accuracy.
Configure JWT extraction from cookies:
PXConfiguration pxConfiguration = new PXConfiguration.Builder()
...
.pxJwtCookieName("authCookie")
.pxJwtCookieUserIdFieldName("userId")
.pxJwtCookieAdditionalFieldNames(Arrays.asList("email", "role"))
.build();Configure JWT extraction from headers:
PXConfiguration pxConfiguration = new PXConfiguration.Builder()
...
.pxJwtHeaderName("Authorization")
.pxJwtHeaderUserIdFieldName("sub")
.pxJwtHeaderAdditionalFieldNames(Arrays.asList("exp", "iss"))
.build();The SDK will:
- First attempt to extract user identifiers from the configured cookie
- If not found, attempt to extract from the configured header
- Support dot notation for nested fields (e.g., "user.id")
- Automatically handle Bearer token prefixes in headers
For enhanced security in HTTPS-only environments, you can enable the secure flag on the pxhd cookie. This ensures the cookie is only transmitted over secure connections:
PXConfiguration pxConfiguration = new PXConfiguration.Builder()
...
.securedPxhdEnabled(true)
.build();Note Only enable this in environments where all traffic is served over HTTPS, as the cookie will not be sent over HTTP connections when this flag is enabled.
Simply create multiple instances of the PerimeterX class:
PerimeterX enforcerApp1 = new PerimeterX(new PXConfiguration.Builder().appId(APP_ID_1)...build(););
PerimeterX enforcerApp2 = new PerimeterX(new PXConfiguration.Builder().appId(APP_ID_2)...build(););
...
// Inside route request handler for app 1:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOExcption {
PXContext ctx = enforcerApp1.px(req, new HttpServletResponseWrapper(resp);
...
}
...
// Inside route request handler for app 2:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOExcption {
PXContext ctx = enforcerApp2.pxVerify(req, new HttpServletResponseWrapper(resp);
if(ctx != null) {
...
}
}perimeterx-java-sdk is using SLF4J and Logback for logs.
For further information please visit SLF4J and Logback.
If you wish to use a basic logger which uses System.out and System.err to print debug and error accordingly,
Change the value of the static variable to your desired level.
import com.perimeterx.models.configuration.PXConfiguration;
import com.perimeterx.utils.LoggerSeverity;
PXConfiguration.setPxLoggerSeverity(LoggerSeverity.DEBUG);Note This method can be executed once, no need to execute it every request.
The following steps are welcome when contributing to our project.
First and foremost, Create a fork of the repository, and clone it locally. Create a branch on your fork, preferably using a self descriptive branch name.
Code your way out of your mess, and help improve our project by implementing missing features, adding capabilities or fixing bugs.
To run the code, simply follow the steps in the installation guide. Grab the keys from the PerimeterX Portal, and try refreshing your page several times continuously. If no default behaviors have been overriden, you should see the PerimeterX block page. Solve the CAPTCHA to clean yourself and start fresh again.
After you have completed the process, create a pull request to the Upstream repository. Please provide a complete and thorough description explaining the changes. Remember this code has to be read by our maintainers, so keep it simple, smart and accurate.
PerimeterX processes URI paths with general- and sub-delimiters according to RFC 3986. General delimiters (e.g., ?, #) are used to separate parts of the URI. Sub-delimiters (e.g., $, &) are not used to split the URI as they are considered valid characters in the URI path.
