文档章节

给JFinal添加Shiro插件功能,支持Shiro所有注解-实现篇

玛雅牛
 玛雅牛
发布于 2013/06/12 22:59
字数 2752
阅读 15169
收藏 75
点赞 12
评论 8

2015年8月3日更新:

支持JFinal 2.0 版本,同时给出了一些实际代码,想见git

@JFinal给出了一些好的建议,已重构部分代码。

代码放在oschina的git上,访问地址:

http://git.oschina.net/myaniu/jfinalshiroplugin

最近用JFinal做个东西,需要进行较为精细的权限控制,研究后决定用Shiro来实现。于是给JFinal做了一个插件。

做了两版,第一版采用Shiro本身的aop来做,使用拦截器实现,每次请求都要检查注解,根据注解构建5个访问控制拦截器进行处理。仔细研究下,觉得每个请求需要处理的访问控制注解在系统启动时应该能全部获得,何不在启动时构建好。这样性能会好一些。Shiro原有的一套处理不适合在启动时构建好,于是重新设计了,但是具体处理逻辑还是来自Shiro,代码直接搬过来。放代码。

1)改造JFinal类,增加一个获得Routes的方法。

 

2)定义访问控制检查接口

package com.jfinal.ext.plugin.shiro;

import org.apache.shiro.authz.AuthorizationException;

/**
 * 访问控制处理器接口
 * @author dafei
 *
 */
interface AuthzHandler {
	/**
	 * 访问控制检查
	 * @throws AuthorizationException 授权异常
	 */
	public void assertAuthorized()throws AuthorizationException;
}

3)定义访问控制抽象基类。

abstract class AbstractAuthzHandler implements AuthzHandler {

	/**
	 * 获得Shiro的Subject对象。
	 * @return
	 */
	 protected Subject getSubject() {
	     return SecurityUtils.getSubject();
	 }
}

4)定义五种权限检查处理器。

/**
 * 基于角色的访问控制处理器,非单例模式运行。
 * @author dafei
 *
 */
class RoleAuthzHandler extends AbstractAuthzHandler {

	private final Annotation annotation;

	public RoleAuthzHandler(Annotation annotation){
		this.annotation = annotation;
	}

	public void assertAuthorized() throws AuthorizationException {
		//if (!(annotation instanceof RequiresRoles)) return;
        RequiresRoles rrAnnotation = (RequiresRoles) annotation;
        String[] roles = rrAnnotation.value();

        if (roles.length == 1) {
            getSubject().checkRole(roles[0]);
            return;
        }
        if (Logical.AND.equals(rrAnnotation.logical())) {
            getSubject().checkRoles(Arrays.asList(roles));
            return;
        }
        if (Logical.OR.equals(rrAnnotation.logical())) {
            // Avoid processing exceptions unnecessarily - "delay" throwing the exception by calling hasRole first
            boolean hasAtLeastOneRole = false;
            for (String role : roles) if (getSubject().hasRole(role)) hasAtLeastOneRole = true;
            // Cause the exception if none of the role match, note that the exception message will be a bit misleading
            if (!hasAtLeastOneRole) getSubject().checkRole(roles[0]);
        }
	}
}
/**
 * 基于权限的访问控制处理器,非单例模式运行。
 * @author dafei
 *
 */
class PermissionAuthzHandler extends AbstractAuthzHandler {
	private final Annotation annotation;

	public PermissionAuthzHandler(Annotation annotation) {
		this.annotation = annotation;
	}

	public void assertAuthorized() throws AuthorizationException {
		if (!(annotation instanceof RequiresPermissions))
			return;

		RequiresPermissions rpAnnotation = (RequiresPermissions) annotation;
		String[] perms = rpAnnotation.value();
		Subject subject = getSubject();

		if (perms.length == 1) {
			subject.checkPermission(perms[0]);
			return;
		}
		if (Logical.AND.equals(rpAnnotation.logical())) {
			getSubject().checkPermissions(perms);
			return;
		}
		if (Logical.OR.equals(rpAnnotation.logical())) {
			// Avoid processing exceptions unnecessarily - "delay" throwing the
			// exception by calling hasRole first
			boolean hasAtLeastOnePermission = false;
			for (String permission : perms)
				if (getSubject().isPermitted(permission))
					hasAtLeastOnePermission = true;
			// Cause the exception if none of the role match, note that the
			// exception message will be a bit misleading
			if (!hasAtLeastOnePermission)
				getSubject().checkPermission(perms[0]);

		}

	}

}
/**
 * 已认证通过访问控制处理器
 * 单例模式运行。
 *
 * @author dafei
 *
 */
class AuthenticatedAuthzHandler extends AbstractAuthzHandler {

	private static AuthenticatedAuthzHandler aah = new AuthenticatedAuthzHandler();

	private AuthenticatedAuthzHandler(){}

	public static  AuthenticatedAuthzHandler me(){
		return aah;
	}

	public void assertAuthorized() throws AuthorizationException {
		if (!getSubject().isAuthenticated() ) {
            throw new UnauthenticatedException( "The current Subject is not authenticated.  Access denied." );
        }
	}
}
/**
 * 认证通过或已记住的用户访问控制处理器
 * 单例模式运行。
 * @author dafei
 *
 */
class UserAuthzHandler extends AbstractAuthzHandler {
	private static UserAuthzHandler uah = new UserAuthzHandler();

	private UserAuthzHandler(){}

	public static  UserAuthzHandler me(){
		return uah;
	}

	public void assertAuthorized() throws AuthorizationException {
		if (getSubject().getPrincipal() == null) {
            throw new UnauthenticatedException("Attempting to perform a user-only operation.  The current Subject is " +
                    "not a user (they haven't been authenticated or remembered from a previous login).  " +
                    "Access denied.");
        }
	}
}
/**
 * 访客访问控制处理器
 * @author dafei
 *
 */
class GuestAuthzHandler extends AbstractAuthzHandler {
	private static GuestAuthzHandler gah = new GuestAuthzHandler();

	private GuestAuthzHandler(){}

	public static  GuestAuthzHandler me(){
		return gah;
	}

	public void assertAuthorized() throws AuthorizationException {
		 if (getSubject().getPrincipal() != null) {
	            throw new UnauthenticatedException("Attempting to perform a guest-only operation.  The current Subject is " +
	                    "not a guest (they have been authenticated or remembered from a previous login).  Access " +
	                    "denied.");
	        }
	}

}

5)定义一个组合访问处理器,用来统一几个处理器。

class CompositeAuthzHandler implements AuthzHandler {

	private final List<AuthzHandler> authzHandlers;

	public CompositeAuthzHandler(List<AuthzHandler> authzHandlers){
		this.authzHandlers = authzHandlers;
	}

	public void assertAuthorized() throws AuthorizationException {
		for(AuthzHandler authzHandler : authzHandlers){
			authzHandler.assertAuthorized();
		}
	}
}

6)定义了一个注解,用来清除权限注解(主要用来清除Controller上的访问控制注解)

/**
 * 用来清除所有的Shiro访问控制注解,适合于Controller绝大部分方法都需要做访问控制,个别不需要做访问控制的场合。
 * 仅能用在方法上。
 * @author dafei
 */
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ClearShiro {
}

7)实现ShiroPlugin方法

package com.jfinal.ext.plugin.shiro;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresGuest;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.authz.annotation.RequiresUser;

import com.jfinal.config.Routes;
import com.jfinal.core.ActionKey;
import com.jfinal.core.Controller;
import com.jfinal.core.JFinal;
import com.jfinal.plugin.IPlugin;

/**
 * Shiro插件,启动时加载所有Shiro访问控制注解。
 * @author dafei
 *
 */
@SuppressWarnings("unchecked")
public class ShiroPlugin implements IPlugin {

	private static final String SLASH = "/";

	/**
	 * Shiro的几种访问控制注解
	 */
	private static final Class<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES = new Class[] {
			RequiresPermissions.class, RequiresRoles.class, RequiresUser.class,
			RequiresGuest.class, RequiresAuthentication.class };

/**
 * 路由设定
 */
 private final Routes routes;


 /**
 * 构造函数
 * @param routes 路由设定
 */
 public ShiroPlugin(Routes routes){
 this.routes = routes;
 }

	/**
	 * 停止插件
	 */
	public boolean stop() {
		return true;
	}

	/**
	 * 启动插件
	 */
	public boolean start() {
		//获取所有路由设定。这里修改了JFinal类,增加了一个getRoutes方法。
		//Routes routes = JFinal.me().getRoutes();
		Set<String> excludedMethodName = buildExcludedMethodName();
		ConcurrentMap<String, AuthzHandler> authzMaps = new ConcurrentHashMap<String, AuthzHandler>();
		//逐个访问所有注册的Controller,解析Controller及action上的所有Shiro注解。
		//并依据这些注解,actionKey提前构建好权限检查处理器。
		for (Entry<String, Class<? extends Controller>> entry : routes
				.getEntrySet()) {
			Class<? extends Controller> controllerClass = entry.getValue();

			// 获取Controller的所有Shiro注解。
			List<Annotation> controllerAnnotations = getAuthzAnnotations(controllerClass);
                        String controllerKey = entry.getKey();
			// 逐个遍历方法。
			Method[] methods = controllerClass.getMethods();
			for (Method method : methods) {
				//排除掉Controller基类的所有方法,并且只关注没有参数的Action方法。
				if (!excludedMethodName.contains(method.getName())
						&& method.getParameterTypes().length == 0) {
					//若该方法上存在ClearShiro注解,则对该action不进行访问控制检查。
					if(isClearShiroAnnotationPresent(method)){
						continue;
					}
					//获取方法的所有Shiro注解。
					List<Annotation> methodAnnotations = getAuthzAnnotations(method);
					//依据Controller的注解和方法的注解来生成访问控制处理器。
					AuthzHandler authzHandler = createAuthzHandler(
							controllerAnnotations, methodAnnotations);
					//生成访问控制处理器成功。
					if (authzHandler != null) {
						//构建ActionKey,参考ActionMapping中实现
						String actionKey = createActionKey(controllerClass,
								method,controllerKey);
						//添加映射
						authzMaps.put(actionKey, authzHandler);
					}
				}
			}
		}
		//注入到ShiroKit类中。ShiroKit类以单例模式运行。
		ShiroKit.init(authzMaps);
		return true;
	}

	/**
	 * 从Controller方法中构建出需要排除的方法列表
	 * @return
	 */
	private Set<String> buildExcludedMethodName() {
		Set<String> excludedMethodName = new HashSet<String>();
		Method[] methods = Controller.class.getMethods();
		for (Method m : methods) {
			if (m.getParameterTypes().length == 0)
				excludedMethodName.add(m.getName());
		}
		return excludedMethodName;
	}

	/**
	 * 依据Controller的注解和方法的注解来生成访问控制处理器。
	 * @param controllerAnnotations  Controller的注解
	 * @param methodAnnotations 方法的注解
	 * @return 访问控制处理器
	 */
	private AuthzHandler createAuthzHandler(
			List<Annotation> controllerAnnotations,
			List<Annotation> methodAnnotations) {

		//没有注解
		if (controllerAnnotations.size() == 0 && methodAnnotations.size() == 0) {
			return null;
		}
		//至少有一个注解
		List<AuthzHandler> authzHandlers = new ArrayList<AuthzHandler>(5);
		for (int index = 0; index < 5; index++) {
			authzHandlers.add(null);
		}

		// 逐个扫描注解,若是相应的注解则在相应的位置赋值。
		scanAnnotation(authzHandlers, controllerAnnotations);
		// 逐个扫描注解,若是相应的注解则在相应的位置赋值。函数的注解优先级高于Controller
		scanAnnotation(authzHandlers, methodAnnotations);

		// 去除空值
		List<AuthzHandler> finalAuthzHandlers = new ArrayList<AuthzHandler>();
		for (AuthzHandler a : authzHandlers) {
			if (a != null) {
				finalAuthzHandlers.add(a);
			}
		}
		authzHandlers = null;
		// 存在多个,则构建组合AuthzHandler
		if (finalAuthzHandlers.size() > 1) {
			return new CompositeAuthzHandler(finalAuthzHandlers);
		}
		// 一个的话直接返回
		return finalAuthzHandlers.get(0);
	}

	/**
	 * 逐个扫描注解,若是相应的注解则在相应的位置赋值。
	 * 注解的处理是有顺序的,依次为RequiresRoles,RequiresPermissions,
	 * RequiresAuthentication,RequiresUser,RequiresGuest
	 *
	 * @param authzArray
	 * @param annotations
	 */
	private void scanAnnotation(List<AuthzHandler> authzArray,
			List<Annotation> annotations) {
		if (null == annotations || 0 == annotations.size()) {
			return;
		}
		for (Annotation a : annotations) {
			if (a instanceof RequiresRoles) {
				authzArray.set(0, new RoleAuthzHandler(a));
			} else if (a instanceof RequiresPermissions) {
				authzArray.set(1, new PermissionAuthzHandler(a));
			} else if (a instanceof RequiresAuthentication) {
				authzArray.set(2, AuthenticatedAuthzHandler.me());
			} else if (a instanceof RequiresUser) {
				authzArray.set(3, UserAuthzHandler.me());
			} else if (a instanceof RequiresGuest) {
				authzArray.set(4, GuestAuthzHandler.me());
			}
		}
	}

	/**
	 * 构建actionkey,参考ActionMapping中的实现。
	 *
	 * @param controllerClass
	 * @param method
	 * @param controllerKey
	 * @return
	 */
	private String createActionKey(Class<? extends Controller> controllerClass,
			Method method, String controllerKey) {
String methodName = method.getName();
 String actionKey = "";


 ActionKey ak = method.getAnnotation(ActionKey.class);
 if (ak != null) {
 actionKey = ak.value().trim();
 if ("".equals(actionKey))
 throw new IllegalArgumentException(controllerClass.getName() + "." + methodName + "(): The argument of ActionKey can not be blank.");
 if (!actionKey.startsWith(SLASH))
 actionKey = SLASH + actionKey;
 }
 else if (methodName.equals("index")) {
 actionKey = controllerKey;
 }
 else {
 actionKey = controllerKey.equals(SLASH) ? SLASH + methodName : controllerKey + SLASH + methodName;
 }
 return actionKey;

	}

	/**
	 * 返回该方法的所有访问控制注解
	 *
	 * @param method
	 * @return
	 */
	private List<Annotation> getAuthzAnnotations(Method method) {
		List<Annotation> annotations = new ArrayList<Annotation>();
		for (Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES) {
			Annotation a = method.getAnnotation(annClass);
			if (a != null) {
				annotations.add(a);
			}
		}
		return annotations;
	}

	/**
	 * 返回该Controller的所有访问控制注解
	 *
	 * @param method
	 * @return
	 */
	private List<Annotation> getAuthzAnnotations(
			Class<? extends Controller> targetClass) {
		List<Annotation> annotations = new ArrayList<Annotation>();
		for (Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES) {
			Annotation a = targetClass.getAnnotation(annClass);
			if (a != null) {
				annotations.add(a);
			}
		}
		return annotations;
	}
	/**
	 * 该方法上是否有ClearShiro注解
	 * @param method
	 * @return
	 */
	private boolean isClearShiroAnnotationPresent(Method method) {
		Annotation a = method.getAnnotation(ClearShiro.class);
		if (a != null) {
			return true;
		}
		return false;
	}
}

8)实现Shiro拦截器。

package com.jfinal.ext.plugin.shiro;

import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthenticatedException;

import com.jfinal.aop.Interceptor;
import com.jfinal.core.ActionInvocation;

public class ShiroInterceptor implements Interceptor {

	public void intercept(Invocation ai){                 AuthzHandler  ah = ShiroKit.getAuthzHandler(ai.getActionKey());

		//存在访问控制处理器。
		if(ah != null){
	        try {
	        	//执行权限检查。
	        	ah.assertAuthorized();
			} catch (UnauthenticatedException lae) {
				//RequiresGuest,RequiresAuthentication,RequiresUser,未满足时,抛出未经授权的异常。
				//如果没有进行身份验证,返回HTTP401状态码
				ai.getController().renderError(401);
				return;
			} catch (AuthorizationException ae) {
				//RequiresRoles,RequiresPermissions授权异常
				//如果没有权限访问对应的资源,返回HTTP状态码403。
				ai.getController().renderError(403);
				return;
			} catch (Exception e) {
				ai.getController().renderError(401); return;
			}
        }
        //执行正常逻辑
        ai.invoke();
	}
}

9)构建一个ShiroKit辅助类

package com.jfinal.ext.plugin.shiro;

import java.util.concurrent.ConcurrentMap;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;


/**
 * 将所有Shiro指令封装成HTTL的函数。
 *
 * @author dafei
 */
public class ShiroKit {

	/**
	 * 用来记录那个action或者actionpath中是否有shiro认证注解。
	 */
	private static ConcurrentMap<String, AuthzHandler> authzMaps = null;


	private static final String NAMES_DELIMETER = ",";

	/**
	 * 禁止初始化
	 */
	private ShiroKit() {}

	static void init(ConcurrentMap<String, AuthzHandler> maps) {
		authzMaps = maps;
	}

	static AuthzHandler getAuthzHandler(String actionKey){
		return authzMaps.get(actionKey);
	}

	/**
	 * 获取 Subject
	 *
	 * @return Subject
	 */
	protected static Subject getSubject() {
		return SecurityUtils.getSubject();
	}

	/**
	 * 验证当前用户是否属于该角色?,使用时与lacksRole 搭配使用
	 *
	 * @param roleName
	 *            角色名
	 * @return 属于该角色:true,否则false
	 */
	public static boolean hasRole(String roleName) {
		return getSubject() != null && roleName != null
				&& roleName.length() > 0 && getSubject().hasRole(roleName);
	}

	/**
	 * 与hasRole标签逻辑相反,当用户不属于该角色时验证通过。
	 *
	 * @param roleName
	 *            角色名
	 * @return 不属于该角色:true,否则false
	 */
	public static boolean lacksRole(String roleName) {
		return !hasRole(roleName);
	}

	/**
	 * 验证当前用户是否属于以下任意一个角色。
	 *
	 * @param roleNames
	 *            角色列表
	 * @return 属于:true,否则false
	 */
	public static boolean hasAnyRoles(String roleNames) {
		boolean hasAnyRole = false;
		Subject subject = getSubject();
		if (subject != null && roleNames != null && roleNames.length() > 0) {
			// Iterate through roles and check to see if the user has one of the
			// roles
			for (String role : roleNames.split(NAMES_DELIMETER)) {
				if (subject.hasRole(role.trim())) {
					hasAnyRole = true;
					break;
				}
			}
		}
		return hasAnyRole;
	}

	/**
	 * 验证当前用户是否属于以下所有角色。
	 *
	 * @param roleNames
	 *            角色列表
	 * @return 属于:true,否则false
	 */
	public static boolean hasAllRoles(String roleNames) {
		boolean hasAllRole = true;
		Subject subject = getSubject();
		if (subject != null && roleNames != null && roleNames.length() > 0) {
			// Iterate through roles and check to see if the user has one of the
			// roles
			for (String role : roleNames.split(NAMES_DELIMETER)) {
				if (!subject.hasRole(role.trim())) {
					hasAllRole = false;
					break;
				}
			}
		}
		return hasAllRole;
	}

	/**
	 * 验证当前用户是否拥有指定权限,使用时与lacksPermission 搭配使用
	 *
	 * @param permission
	 *            权限名
	 * @return 拥有权限:true,否则false
	 */
	public static boolean hasPermission(String permission) {
		return getSubject() != null && permission != null
				&& permission.length() > 0
				&& getSubject().isPermitted(permission);
	}

	/**
	 * 与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过。
	 *
	 * @param permission
	 *            权限名
	 * @return 拥有权限:true,否则false
	 */
	public static boolean lacksPermission(String permission) {
		return !hasPermission(permission);
	}

	/**
	 * 已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。与notAuthenticated搭配使用
	 *
	 * @return 通过身份验证:true,否则false
	 */
	public static boolean authenticated() {
		return getSubject() != null && getSubject().isAuthenticated();
	}

	/**
	 * 未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。。
	 *
	 * @return 没有通过身份验证:true,否则false
	 */
	public static boolean notAuthenticated() {
		return !authenticated();
	}

	/**
	 * 认证通过或已记住的用户。与guset搭配使用。
	 *
	 * @return 用户:true,否则 false
	 */
	public static boolean user() {
		return getSubject() != null && getSubject().getPrincipal() != null;
	}

	/**
	 * 验证当前用户是否为“访客”,即未认证(包含未记住)的用户。用user搭配使用
	 *
	 * @return 访客:true,否则false
	 */
	public static boolean guest() {
		return !user();
	}

	/**
	 * 输出当前用户信息,通常为登录帐号信息。
	 * @return 当前用户信息
	 */
	public String principal(){
		if (getSubject() != null) {
            // Get the principal to print out
            Object principal = getSubject().getPrincipal();
            return principal.toString();
        }
		return "";
	}
}

实现完毕。

----------------------------------------------------------

玛雅牛

 

© 著作权归作者所有

共有 人打赏支持
玛雅牛

玛雅牛

粉丝 477
博文 108
码字总数 27153
作品 4
高级程序员
加载中

评论(8)

程俊87
jfinal 3 以上 routes.getEntrySet() 不存在 ,可有提供修改的解決方案
Solr_
Solr_
为什么不放出一个例子?
懒的去懒
懒的去懒
楼主你好,有没有一个完整的整合jfinal应用例子?
罗盛力
罗盛力
楼主可以考虑ShiroInterceptor里面的catch中加入判断请求类型以适应Ajax请求的情况,或者直接在拦截器里面抛出异常,这样方便做异常的管理
玛雅牛
玛雅牛

引用来自“铂金小虫”的评论

shiro本身不带aop吧,是依赖其他的aop框架吧。比如和spring整合。

shiro本身独立,但是又针对这种框架的插件。
铂金小虫
铂金小虫
shiro本身不带aop吧,是依赖其他的aop框架吧。比如和spring整合。
玛雅牛
玛雅牛

引用来自“JFinal”的评论

终于有牛人集成了Shiro,顶一个。作者自己没用过Shrio,给不到太多建议。有几个地方提一下,Routes可以在YourJFinalConfig.configRoute(Routes me)方法中将 me 存放在一个static变量中,这样就可以得到了,不必修改JFinal源码。另外,拦截器里面为啥不用ai.getActionKey()来得actionKey,可能是别的原因吧

对JFinal的源码还不是非常熟悉,Routes获取的方式,你说的这种对JFinal侵入少一些,我该下,拦截器里刚开始是用ai.getActionKey()来做的,还是由于对JFinal代码不熟,才改成现在的方式,多谢你的指点,采用ai.getActionKey()话,性能应该会好一些。
JFinal
JFinal
终于有牛人集成了Shiro,顶一个。作者自己没用过Shrio,给不到太多建议。有几个地方提一下,Routes可以在YourJFinalConfig.configRoute(Routes me)方法中将 me 存放在一个static变量中,这样就可以得到了,不必修改JFinal源码。另外,拦截器里面为啥不用ai.getActionKey()来得actionKey,可能是别的原因吧
Jboot v1.4.9 发布,核心 JFinal 升级到 3.4 最新版本

Jboot 是一个基于 JFinal 和 Undertow 开发的微服务框架。提供了 AOP、RPC、分布式缓存、限流、降级、熔断、统一配置中心、Opentracing 数据追踪、metrics 数据监控、分布式 session、代码生...

理工男海哥 ⋅ 05/03 ⋅ 0

Jboot v1.5.3 发布,修复 bug 和优化缓存

Jboot 是一个基于 JFinal 和 Undertow 开发的微服务框架。提供了 AOP、RPC、分布式缓存、限流、降级、熔断、统一配置中心、Opentracing 数据追踪、metrics 数据监控、分布式 session、代码生...

理工男海哥 ⋅ 05/17 ⋅ 0

Jboot v1.5.5 发布,修复 shiro 指令等若干bug

Jboot 是一个基于 JFinal 和 Undertow 开发的微服务框架。提供了 AOP、RPC、分布式缓存、限流、降级、熔断、统一配置中心、Opentracing 数据追踪、metrics 数据监控、分布式 session、代码生...

理工男海哥 ⋅ 06/01 ⋅ 0

Jboot v1.4.7 发布,新增Shiro使用分布式Session的支持

Jboot 是一个基于 JFinal 和 Undertow 开发的微服务框架。提供了 AOP、RPC、分布式缓存、限流、降级、熔断、统一配置中心、Opentracing 数据追踪、metrics 数据监控、分布式 session、代码生...

理工男海哥 ⋅ 04/18 ⋅ 0

JFinal 3.4 发布,将极速贯彻到 UI 层

jfinal 的终极目标是全面实现软件开发整个过程的极速开发,极大提升开发效率,极大降低学习成本,极大提升开发体验 jfinal 诞生头五年,已实现 WEB + ORM + AOP 层面的极速开发,赢得了大量开...

JFinal ⋅ 04/28 ⋅ 129

JAVA 极速WEB+ORM框架 - JFinal

JFinal 是基于 Java 语言的极速 WEB + ORM 框架,其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展、Restful。在拥有Java语言所有优势的同时再拥有ruby、python、p...

JFinal ⋅ 2012/03/18 ⋅ 496

shiro 登陆过滤器获取不到header头信息

前端用的是ajax提交请求,jfinal整合shiro的登陆过滤器里得不到request的header的X-Requested-With,不知道为什么

littlerd ⋅ 05/08 ⋅ 0

给JFinal添加Shiro插件功能,支持Shiro所有注解

个人实现了给JFinal添加Shiro插件功能,支持Shiro所有注解。 写了三篇博客来介绍,还请@JFinal指点。 给JFinal添加Shiro插件功能,支持Shiro所有注解-实现篇 给JFinal添加Shiro插件功能,支持...

玛雅牛 ⋅ 2013/06/13 ⋅ 9

Jboot v1.5.7 发布,优化细节、正常更新

Jboot 是一个基于 JFinal 和 Undertow 开发的微服务框架。提供了 AOP、RPC、分布式缓存、限流、降级、熔断、统一配置中心、Opentracing 数据追踪、metrics 数据监控、分布式 session、代码生...

理工男海哥 ⋅ 06/15 ⋅ 0

Jboot v1.5.0 发布,升级 JFinal 和 优化细节

Jboot 是一个基于 JFinal 和 Undertow 开发的微服务框架。提供了 AOP、RPC、分布式缓存、限流、降级、熔断、统一配置中心、Opentracing 数据追踪、metrics 数据监控、分布式 session、代码生...

理工男海哥 ⋅ 05/09 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

idea 整合 vue 启动

刚学习Vue 搭建了一个项目 只能命令启动 Idea里面不会启动 尝试了一下修改启动的配置 如下: 1.首先你要保证你的package.json没有修改过 具体原因没有看 因为我改了这个name的值 就没办法启动...

事儿爹 ⋅ 23分钟前 ⋅ 0

数据仓库技术概述(一看就是架构师写的,对我极其有用)

ETL,是英文 Extract-Transform-Load 的缩写,用来描述将数据从来源端经过抽取(extract)、交互转换(transform)、加载(load)至目的端的过程。ETL一词较常用在数据仓库,但其对象并不限于...

gulf ⋅ 25分钟前 ⋅ 0

redis在windows环境的后台运行方法

在后台运行,首先需要安装redis服务,命令为 redis-server.exe --service-install redis.windows.conf --loglevel verbose 启动,命令为 redis-server --service-start 停止,命令为 redis-...

程序羊 ⋅ 27分钟前 ⋅ 0

比特币现金开发者提出新的交易订单规则

本周,四位比特币现金的四位开发者和研究员:Joannes Vermorel(Lokad),AmaurySéchet(比特币ABC),Shammah Chancellor(比特币ABC)和Tomas van der Wansem(Bitcrust)共同发表了一篇关...

lpy411 ⋅ 30分钟前 ⋅ 0

vue获取input输入框的数据

用惯了jQuery,突然使用vue感觉很不习惯,有很多不同的地方,感觉是两个不同的思想来写前端的代码。jQuery是使用选择器($)选取DOM对象,对其进行赋值、取值、事件绑定等操作。而Vue则是通过...

王子城 ⋅ 32分钟前 ⋅ 0

竟然这就是面向对象的游戏设计?!

从程序角度考虑,许多 JavaScript 都基于循环和大量的 if/else 语句。在本文中,我们可了解一种更聪明的做法 — 在 JavaScript 游戏中使用面向对象来设计。本文将概述原型继承和使用 JavaSc...

柳猫 ⋅ 37分钟前 ⋅ 2

git cmd git bash

刚用到了Git,看到windows环境下有两个命令输入窗口 第一个是可视化图形界面,第二个是CMD,第三个是Bash。 Git中的Bash是基于CMD的,在CMD的基础上增添一些新的命令与功能。所以建议在使用的...

东东笔记 ⋅ 40分钟前 ⋅ 0

分布式系统CAP和Base

1、分布式系统 1.1 简介 由多台计算机和通信的软件组件通过计算机网络连接(本地网络或广域网)组成。分布式系统是建立在网络之上的软件系统。正是因为软件的特性,所以分布式系统具有高度的...

xixingzhe ⋅ 50分钟前 ⋅ 0

查看磁盘占用情况

记一次jenkins构建失败的问题 Build step 'Send build artifacts over SSH' changed build result to UNSTABLE 网上查资料都没明确表明是什么错,回忆之前处理这样的问题。第一时间想到的是不...

ManderSF ⋅ 52分钟前 ⋅ 0

数据库管理提速:SQL解析的探索与应用

前言: SQL解析是一项复杂的技术,一般都是由数据库厂商来掌握,当然也有公司专门提供SQL解析的API。SQL解析与优化是属于编译器范畴,和C语言等其他语言的解析没有本质的区别。其中分为词法分...

java高级架构牛人 ⋅ 59分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部