You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于JAAS实现角色权限控制:如何通过Principal类完成授权?

JAAS 授权控制实现方案(基于Principal)

核心思路

通过给JAAS的Subject添加角色类型的Principal,在执行操作前验证当前Subject是否包含对应权限的角色Principal,实现细粒度的授权控制。具体需要:

  • 区分用户身份Principal与角色身份Principal
  • 登录阶段根据用户分配对应角色并添加到Subject
  • 操作执行前校验角色权限

代码修改步骤

1. 创建角色Principal类

新增RolePrincipal类,专门用于标识用户的角色权限:

import java.io.Serializable;
import java.security.Principal;

public class RolePrincipal implements Principal, Serializable {
    private static final long serialVersionUID = 1L;
    private final String roleName;

    public RolePrincipal(String roleName) {
        this.roleName = roleName;
    }

    @Override
    public String getName() {
        return roleName;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        RolePrincipal that = (RolePrincipal) obj;
        return roleName.equals(that.roleName);
    }

    @Override
    public int hashCode() {
        return roleName.hashCode();
    }
}

2. 完善用户Principal类

修正原LorduPrinciple的序列化ID拼写错误,完善hashCode方法(符合Principal规范):

import java.io.Serializable;
import java.security.Principal;

public class LorduPrinciple implements Principal, Serializable {
    private static final long serialVersionUID = 1L; // 修正拼写错误
    private final String username;

    public LorduPrinciple(String username) {
        this.username = username;
    }

    @Override
    public String getName() {
        return username;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        LorduPrinciple that = (LorduPrinciple) obj;
        return username.equals(that.username);
    }

    @Override
    public int hashCode() {
        return username.hashCode();
    }
}

3. 修改LoginModule,添加角色Principal

在LoLoginModule中扩展用户数据,增加角色字段,登录成功后将用户Principal和角色Principal同时添加到Subject:

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import java.io.IOException;
import java.util.Map;

public class LoLoginModule implements LoginModule {
    // 扩展用户数据,新增角色列
    public static final String[][] TEST_USERS = {
            {"admin", "password", "admin"},
            {"admin2", "password2", "admin"},
            {"user1", "userpass", "users"}
    };

    private Subject subject = null;
    private CallbackHandler callbackHandler = null;
    private LorduPrinciple userPrincipal = null;
    private RolePrincipal rolePrincipal = null;

    @Override
    public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
        this.subject = subject;
        this.callbackHandler = callbackHandler;
        System.out.println("Lordu login module initialized....");
    }

    @Override
    public boolean login() throws LoginException {
        boolean flag = false;
        System.out.println("Login...");
        Callback[] callbackArray = new Callback[2];
        callbackArray[0] = new NameCallback("User Name :");
        callbackArray[1] = new PasswordCallback("Password: ", false);

        try {
            callbackHandler.handle(callbackArray);
            String username = ((NameCallback) callbackArray[0]).getName();
            String password = new String(((PasswordCallback) callbackArray[1]).getPassword());

            for (String[] user : TEST_USERS) {
                if (user[0].equals(username) && user[1].equals(password)) {
                    userPrincipal = new LorduPrinciple(username);
                    rolePrincipal = new RolePrincipal(user[2]);
                    System.out.println("Authentication Success");
                    flag = true;
                    break;
                }
            }

            if (!flag) throw new FailedLoginException("Authentication failure...");
        } catch (IOException | javax.security.auth.callback.UnsupportedCallbackException e) {
            throw new LoginException(e.getMessage());
        }
        return flag;
    }

    @Override
    public boolean commit() throws LoginException {
        boolean flag = false;
        System.out.println("Commit...");
        if (subject != null) {
            if (!subject.getPrincipals().contains(userPrincipal)) {
                subject.getPrincipals().add(userPrincipal);
            }
            if (!subject.getPrincipals().contains(rolePrincipal)) {
                subject.getPrincipals().add(rolePrincipal);
            }
            flag = true;
        }
        return flag;
    }

    @Override
    public boolean abort() throws LoginException {
        System.out.println("Abort module implementing...");
        if (subject != null) {
            if (userPrincipal != null && subject.getPrincipals().contains(userPrincipal)) {
                subject.getPrincipals().remove(userPrincipal);
            }
            if (rolePrincipal != null && subject.getPrincipals().contains(rolePrincipal)) {
                subject.getPrincipals().remove(rolePrincipal);
            }
        }
        subject = null;
        userPrincipal = null;
        rolePrincipal = null;
        return true;
    }

    @Override
    public boolean logout() throws LoginException {
        System.out.println("Logout module implementing....");
        if (subject != null) {
            subject.getPrincipals().remove(userPrincipal);
            subject.getPrincipals().remove(rolePrincipal);
        }
        subject = null;
        return true;
    }
}

4. 修正CallbackHandler类名匹配

原Driver中使用LoCallbackHandler,调整回调类实现为无参构造(从控制台读取输入):

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class LoCallbackHandler implements CallbackHandler {
    @Override
    public void handle(Callback[] callbackArray) throws IOException, javax.security.auth.callback.UnsupportedCallbackException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        for (Callback callback : callbackArray) {
            if (callback instanceof NameCallback) {
                NameCallback nameCallback = (NameCallback) callback;
                System.out.print(nameCallback.getPrompt());
                nameCallback.setName(br.readLine().trim());
            } else if (callback instanceof PasswordCallback) {
                PasswordCallback passwordCallback = (PasswordCallback) callback;
                System.out.print(passwordCallback.getPrompt());
                passwordCallback.setPassword(br.readLine().trim().toCharArray());
            } else {
                throw new javax.security.auth.callback.UnsupportedCallbackException(callback, "Unsupported callback type");
            }
        }
    }
}

5. 在Driver中添加权限检查逻辑

修改performAction方法,执行操作前校验角色权限:

import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.Principal;

public class Driver {
    public enum Action {action1, action2, logout};

    public static void main(String[] args) {
        Driver driver = new Driver();
        System.setProperty("java.security.auth.login.config", "Lord.config");
        LoginContext loginContext = null;
        while (true) {
            try {
                loginContext = new LoginContext("LorduJaas", new LoCallbackHandler());
                loginContext.login();
                boolean flag = true;
                while (flag) {
                    flag = driver.performAction(loginContext);
                }
            } catch (LoginException | IOException e) {
                System.out.println(e.getMessage());
            }
        }
    }

    boolean performAction(LoginContext loginContext) throws IOException, LoginException {
        boolean flag = true;
        System.out.println("Please specify your action (Usage: action1,action2,logout)");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String actionInput = br.readLine().trim();

        try {
            Action action = Action.valueOf(actionInput);
            Subject subject = loginContext.getSubject();

            switch (action) {
                case logout:
                    loginContext.logout();
                    System.out.println("Logged out");
                    flag = false;
                    break;
                case action1:
                    if (hasRole(subject, "admin")) {
                        Subject.doAs(subject, (PrivilegedAction<Object>) () -> {
                            System.out.println("Action 1 done..");
                            return null;
                        });
                        System.out.println("By " + getUsername(subject));
                    } else {
                        System.out.println("Permission denied: Only admin can perform action1");
                    }
                    break;
                case action2:
                    if (hasRole(subject, "admin") || hasRole(subject, "users")) {
                        Subject.doAs(subject, (PrivilegedAction<Object>) () -> {
                            System.out.println("Action 2 done..");
                            return null;
                        });
                        System.out.println("By " + getUsername(subject));
                    } else {
                        System.out.println("Permission denied: You don't have permission to perform action2");
                    }
                    break;
            }
        } catch (IllegalArgumentException ex) {
            System.out.println("Invalid action: " + actionInput);
        }
        return flag;
    }

    // 检查Subject是否包含指定角色
    private boolean hasRole(Subject subject, String roleName) {
        for (Principal p : subject.getPrincipals(RolePrincipal.class)) {
            if (p.getName().equals(roleName)) {
                return true;
            }
        }
        return false;
    }

    // 获取当前登录用户名
    private String getUsername(Subject subject) {
        for (Principal p : subject.getPrincipals(LorduPrinciple.class)) {
            return p.getName();
        }
        return "unknown";
    }
}

配置文件说明(Lord.config)

确保配置文件指向正确的LoginModule:

LorduJaas {
    你的包名.LoLoginModule required;
};

测试流程

  1. 用admin/admin2登录,可执行所有操作(action1、action2、logout)
  2. 用user1登录,仅能执行action2和logout,执行action1会提示权限拒绝

内容的提问来源于stack exchange,提问作者hariharan baskaran

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 19:48:17