com.yea.shiro.realm.AbstractRealm.java Source code

Java tutorial

Introduction

Here is the source code for com.yea.shiro.realm.AbstractRealm.java

Source

/**
 * Copyright 2017 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.yea.shiro.realm;

import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import com.yea.core.exception.YeaException;
import com.yea.core.remote.struct.CallAct;
import com.yea.core.shiro.model.UserPrincipal;
import com.yea.shiro.constants.ShiroConstants;
import com.yea.shiro.constants.ShiroConstants.ShiroColumn;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

/**
 * 
 * @author yiyongfei
 *
 */
public abstract class AbstractRealm extends AuthorizingRealm {
    private static final Logger log = LoggerFactory.getLogger(AbstractRealm.class);

    /**
     * Password hash salt configuration. <ul>
     *   <li>NO_SALT - password hashes are not salted.</li>
     *   <li>COLUMN - salt is in a separate column in the database.</li></ul>
     */
    public enum SaltStyle {
        NO_SALT, COLUMN
    };

    protected boolean permissionsLookupEnabled = false;

    private SaltStyle saltStyle = SaltStyle.COLUMN;

    /**
     * Enables lookup of permissions during authorization.  The default is "false" - meaning that only roles
     * are associated with a user.  Set this to true in order to lookup roles <b>and</b> permissions.
     *
     * @param permissionsLookupEnabled true if permissions should be looked up during authorization, or false if only
     *                                 roles should be looked up.
     */
    public void setPermissionsLookupEnabled(boolean permissionsLookupEnabled) {
        this.permissionsLookupEnabled = permissionsLookupEnabled;
    }

    public void setSaltStyle(SaltStyle saltStyle) {
        this.saltStyle = saltStyle;
    }

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String username = upToken.getUsername();
        if (username == null) {
            throw new AccountException("???????");
        }

        Map<String, Object> mapUser = getUser(username);
        String password = null;
        String salt = null;
        switch (saltStyle) {
        case NO_SALT:
            password = ((String) mapUser.get(ShiroConstants.ShiroColumn.LOGIN_PASSWORD.value())).trim();
            break;
        case COLUMN:
            password = ((String) mapUser.get(ShiroConstants.ShiroColumn.LOGIN_PASSWORD.value())).trim();
            salt = ((String) mapUser.get(ShiroConstants.ShiroColumn.LOGIN_SALT.value())).trim();
        }
        if (password == null) {
            throw new UnknownAccountException("??[" + username + "]??");
        }
        if (ShiroConstants.LockTag.LOCK.value()
                .equals(mapUser.get(ShiroConstants.ShiroColumn.LOGIN_LOCK_TAG.value()))) {
            throw new LockedAccountException("?[" + username + "]??");
        }
        UserPrincipal user = new UserPrincipal();
        user.setLoginName(username);
        user.setPartyId((Long) mapUser.get(ShiroConstants.ShiroColumn.LOGIN_ID.value()));
        user.setPersonName((String) mapUser.get(ShiroConstants.ShiroColumn.LOGIN_PERSON_NAME.value()));
        user.setIsLock((String) mapUser.get(ShiroConstants.ShiroColumn.LOGIN_LOCK_TAG.value()));

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password.toCharArray(), getName());
        if (salt != null) {
            info.setCredentialsSalt(ByteSource.Util.bytes(salt));
        }

        return info;
    }

    protected abstract Map<String, Object> getUser(String username) throws AuthenticationException;

    /**
     * This implementation of the interface expects the principals collection to return a String username keyed off of
     * this realm's {@link #getName() name}
     *
     * @see #getAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //null usernames are invalid
        if (principals == null) {
            //PrincipalCollection method argument cannot be null.
            throw new AuthorizationException("??NULL?");
        }

        UserPrincipal user = (UserPrincipal) getAvailablePrincipal(principals);

        Set<String> roleNames = null;
        Set<String> permissions = null;
        try {
            Set<String>[] rolepermission = getRolePermissionsForUser(user.getLoginName());
            roleNames = rolepermission[0];
            permissions = rolepermission[1];
        } catch (Throwable e) {
            final String message = "[" + user.getLoginName()
                    + "]??????";
            if (log.isErrorEnabled()) {
                log.error(message, e);
            }
            throw new AuthorizationException(message, e);
        }

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);
        info.setStringPermissions(permissions);
        return info;
    }

    @SuppressWarnings("unchecked")
    protected Set<String>[] getRolePermissionsForUser(String username) throws Throwable {
        Set<String> roleNames = new LinkedHashSet<String>();
        Set<String> permissions = null;
        if (permissionsLookupEnabled) {
            permissions = new LinkedHashSet<String>();
        }

        CallAct act = new CallAct();
        act.setActName("shiroAct");
        List<Map<String, Object>> listRole = getRoles(username);
        for (Map<String, Object> mapRole : listRole) {
            roleNames.add(String.valueOf(mapRole.get(ShiroColumn.ROLE_NAME.value())));
            if (permissions != null) {
                List<Map<String, String>> listPermission = getPermissions(mapRole.get(ShiroColumn.ROLE_ID.value()));
                for (Map<String, String> mapPermission : listPermission) {
                    permissions.add(mapPermission.get(ShiroColumn.PERMISSION_CONTENT.value()));
                    if (!StringUtils.isEmpty(mapPermission.get(ShiroColumn.PERMISSION_NAME.value()))) {
                        permissions.add(mapPermission.get(ShiroColumn.PERMISSION_NAME.value()));
                    }
                }
            }
        }
        return new Set[] { roleNames, permissions };
    }

    protected abstract List<Map<String, Object>> getRoles(String username) throws Throwable;

    protected abstract List<Map<String, String>> getPermissions(Object roleId) throws Throwable;

    //???
    private static final Map<String, Integer> expMap = new HashMap<String, Integer>() {
        private static final long serialVersionUID = 1L;
        {
            put("not", 0);
            put("!", 0);

            put("and", 0);
            put("&&", 0);

            put("or", 0);
            put("||", 0);

            put("(", 1);
            put(")", 1);
        }
    };
    private static final Set<String> expList = expMap.keySet();

    @Override
    public boolean isPermitted(PrincipalCollection principals, String permission) {
        Stack<String> exp = getExp(expList, permission);
        if (exp.size() == 1) {
            return super.isPermitted(principals, exp.pop());
        }
        List<String> expTemp = new ArrayList<>();
        //????true , false
        for (String temp : exp) {
            if (expList.contains(temp)) {
                expTemp.add(temp);
            } else {
                expTemp.add(Boolean.toString(super.isPermitted(principals, temp)));
            }
        }
        //
        return computeRpn(expList, expTemp);
    }

    private static boolean computeRpn(Collection<String> expList, Collection<String> exp) {
        log.debug("RPN  exp :{}", exp);
        Stack<Boolean> stack = new Stack<>();
        for (String temp : exp) {
            if (expList.contains(temp)) {
                if ("!".equals(temp) || "not".equals(temp)) {
                    stack.push(!stack.pop());
                } else if ("and".equals(temp) || "&&".equals(temp)) {
                    Boolean s1 = stack.pop();
                    Boolean s2 = stack.pop();
                    stack.push(s1 && s2);
                } else {
                    Boolean s1 = stack.pop();
                    Boolean s2 = stack.pop();
                    stack.push(s1 || s2);
                }
            } else {
                stack.push(Boolean.parseBoolean(temp));
            }
        }
        if (stack.size() > 1) {
            throw new YeaException("compute error? stack: " + exp.toString());
        } else {
            return stack.pop();
        }
    }

    //?
    private static Stack<String> getExp(Collection<String> expList, String exp) {
        Stack<String> s1 = new Stack<>();
        Stack<String> s2 = new Stack<>();
        for (String str : exp.split(" ")) {
            str = str.trim();
            String strL = str.toLowerCase();
            if ("".equals(str)) {
                continue;
            }
            if ("(".equals(str)) {
                //?
                s1.push(str);
            } else if (")".equals(str)) {
                //??
                while (!s1.empty()) {
                    String temp = s1.pop();
                    if ("(".equals(temp)) {
                        break;
                    } else {
                        s2.push(temp);
                    }
                }
            } else if (expList.contains(strL)) {
                //?
                if (s1.empty()) {
                    s1.push(strL);
                } else {
                    String temp = s1.peek();
                    if ("(".equals(temp) || ")".equals(temp)) {
                        s1.push(strL);
                    } else if (expMap.get(strL) >= expMap.get(temp)) {
                        s1.push(strL);
                    } else {
                        s2.push(s1.pop());
                        s1.push(strL);
                    }
                }
            } else {
                //?
                s2.push(str);
            }
        }
        while (!s1.empty()) {
            s2.push(s1.pop());
        }
        return s2;
    }
}