JAAS authorization uses Policy files to grant permissions to authenticated principals. Combined with Subject.doAs(), it controls what code a user can execute.

Permission Model

Java SE uses java.security.Permission subclasses:

Permission Controls
FilePermission File read/write/execute
SocketPermission Network connections
RuntimePermission System operations
AllPermission Everything (admin)

Policy File

policy:

  grant principal com.example.UserPrincipal "alice" {
    permission java.io.FilePermission "/home/alice/-", "read,write";
    permission java.net.SocketPermission "api.example.com:443", "connect";
};

grant principal com.example.UserPrincipal "admin" {
    permission java.security.AllPermission;
};

grant codeBase "file:/app/lib/-" {
    permission java.io.FilePermission "/tmp/-", "read,write";
};
  

Enable with:

  java -Djava.security.policy=policy MyApplication
  

Subject.doAs()

Execute code with a subject’s permissions:

  Subject subject = lc.getSubject();

Subject.doAs(subject, (PrivilegedAction<String>) () -> {
    // Runs with alice's permissions
    try {
        Files.readString(Path.of("/home/alice/data.txt"));
        return "success";
    } catch (IOException e) {
        return "denied";
    }
});
  

PrivilegedAction vs PrivilegedExceptionAction

  // No checked exceptions
String result = Subject.doAs(subject, (PrivilegedAction<String>) () -> "done");

// With checked exceptions
Subject.doAs(subject, (PrivilegedExceptionAction<Void>) () -> {
    socket.connect(new InetSocketAddress("api.example.com", 443));
    return null;
});
  

Custom Permission

  public class OrderPermission extends Permission {
    private final String action;

    public OrderPermission(String name, String action) {
        super(name);  // e.g., "order:123"
        this.action = action;
    }

    @Override
    public boolean implies(Permission p) {
        if (!(p instanceof OrderPermission)) return false;
        OrderPermission other = (OrderPermission) p;
        return (getName().equals("*") || getName().equals(other.getName()))
            && (action.equals("*") || action.equals(other.action));
    }

    @Override
    public boolean equals(Object obj) { /* ... */ }
    @Override
    public int hashCode() { /* ... */ }
    @Override
    public String getActions() { return action; }
}
  

Policy.refresh()

Reload policy without restart:

  Policy.getPolicy().refresh();
  

Modern Alternatives

JAAS policy-based authorization is largely superseded by:

  • Spring Security method/URL security
  • Java Security Manager (deprecated since Java 17, removed in Java 24)
  • Container-level RBAC (Kubernetes, cloud IAM)

Best Practices

  • Treat JAAS policy as legacy — use Spring Security for new projects
  • Principle of least privilege: grant minimum permissions needed
  • Separate authentication (LoginModule) from authorization (Policy)
  • Audit policy files — they are security-critical configuration
  • Test permission boundaries with integration tests using doAs()