001/**
002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.jboot.support.shiro;
017
018import com.jfinal.aop.Invocation;
019import com.jfinal.config.Routes;
020import com.jfinal.core.Controller;
021import com.jfinal.template.expr.ast.MethodKeyBuilder;
022import io.jboot.Jboot;
023import io.jboot.exception.JbootIllegalConfigException;
024import io.jboot.support.shiro.processer.*;
025import io.jboot.utils.ArrayUtil;
026import io.jboot.utils.ClassUtil;
027import io.jboot.utils.StrUtil;
028import org.apache.shiro.authz.annotation.*;
029
030import java.lang.annotation.Annotation;
031import java.lang.reflect.Method;
032import java.util.List;
033import java.util.concurrent.ConcurrentHashMap;
034
035/**
036 * shiro 管理器.
037 */
038public class JbootShiroManager {
039    private final static JbootShiroManager me = new JbootShiroManager();
040
041    private final JbootShiroConfig jbootShiroConfig = Jboot.config(JbootShiroConfig.class);
042
043    private final ShiroRequiresAuthenticationProcesser requiresAuthenticationProcessor = new ShiroRequiresAuthenticationProcesser();
044    private final ShiroRequiresUserProcesser requiresUserProcessor = new ShiroRequiresUserProcesser();
045    private final ShiroRequiresGuestProcesser requiresGuestProcessor = new ShiroRequiresGuestProcesser();
046
047    private JbootShiroManager() {
048    }
049
050
051    public static JbootShiroManager me() {
052        return me;
053    }
054
055    private ConcurrentHashMap<Long, ShiroAuthorizeProcesserInvoker> invokers = new ConcurrentHashMap<>();
056
057
058    public void init(List<Routes.Route> routes) {
059        // do nothing
060    }
061
062    /**
063     * 根据类和方法上的注解生成shiro的注解处理器
064     *
065     * @return 返回是否有shiro处理器,ShiroInterceptorBuilder 根据这一结果来决定是否对方法进行拦截
066     */
067    public boolean buildShiroInvoker(Class clazz, Method method) {
068        if (Controller.class.isAssignableFrom(clazz) &&
069                JbootShiroUtil.getControllerExcludedMethodName().contains(method.getName())) {
070            // 忽略 JbootController 中的方法
071            return false;
072        }
073
074        if (method.getAnnotation(ShiroClear.class) != null) {
075            return false;
076        }
077
078        Annotation[] allAnnotations = ArrayUtil.concat(clazz.getAnnotations(), method.getAnnotations());
079        ShiroAuthorizeProcesserInvoker invoker = new ShiroAuthorizeProcesserInvoker();
080        for (Annotation annotation : allAnnotations) {
081            if (annotation.annotationType() == RequiresPermissions.class) {
082                ShiroRequiresPermissionsProcesser processor = new ShiroRequiresPermissionsProcesser((RequiresPermissions) annotation);
083                invoker.addProcesser(processor);
084            } else if (annotation.annotationType() == RequiresRoles.class) {
085                ShiroRequiresRolesProcesser processor = new ShiroRequiresRolesProcesser((RequiresRoles) annotation);
086                invoker.addProcesser(processor);
087            } else if (annotation.annotationType() == RequiresUser.class) {
088                invoker.addProcesser(requiresUserProcessor);
089            } else if (annotation.annotationType() == RequiresAuthentication.class) {
090                invoker.addProcesser(requiresAuthenticationProcessor);
091            } else if (annotation.annotationType() == RequiresGuest.class) {
092                invoker.addProcesser(requiresGuestProcessor);
093            }
094        }
095
096        if (invoker.getProcessers() != null && invoker.getProcessers().size() > 0) {
097            invokers.put(getMethodKey(method), invoker);
098            return true;
099        }
100
101        return false;
102    }
103
104    public AuthorizeResult invoke(Invocation invocation) {
105        ShiroAuthorizeProcesserInvoker invoker = invokers.get(getMethodKey(invocation.getMethod()));
106        if (invoker == null) {
107            return AuthorizeResult.ok();
108        }
109
110        return invoker.invoke();
111    }
112
113
114    private static MethodKeyBuilder keyBuilder = new MethodKeyBuilder.FastMethodKeyBuilder();
115
116    public static Long getMethodKey(Method method) {
117        return keyBuilder.getMethodKey(method.getDeclaringClass(), method.getName(), method.getParameterTypes());
118    }
119
120
121    private JbootShiroInvokeListener invokeListener;
122
123    public JbootShiroInvokeListener getInvokeListener() {
124
125        if (invokeListener != null) {
126            return invokeListener;
127        }
128
129        invokeListener = JbootShiroInvokeListener.DEFAULT;
130
131        if (StrUtil.isNotBlank(jbootShiroConfig.getInvokeListener())) {
132            invokeListener = ClassUtil.newInstance(jbootShiroConfig.getInvokeListener());
133            if (invokeListener == null) {
134                throw new JbootIllegalConfigException("can not find Class : " + jbootShiroConfig.getInvokeListener() +
135                        " please config jboot.shiro.invokeListener correct. ");
136            }
137        }
138
139        return invokeListener;
140    }
141
142}