最新版本JFinal实现更加灵活路由这块进行了增加设计,原jfinal shiro plugin不支持JFinal 3.0的问题上有很多人也已分享了,只是大多数人只分享了如何将这个插件改造,未对咱们JFinal Template engine 模板引擎支持Shiro标签进行分享,在这我就简单说下,方便有需要的朋友。
1、下载JFinal Shiro 插件:http://git.oschina.net/myaniu/jfinalshiroplugin
(1)、修改ShiroPlugin插件下start方法将:
for (Entry>entry : routes.getEntrySet()) {
Class controllerClass = entry.getValue();
String controllerKey = entry.getKey();
修改为:
for (Routes routes : getRoutesList()) {
for (Routes.Route route : routes.getRouteItemList()) {
Class controllerClass = route.getControllerClass();
String controllerKey = route.getControllerKey();
(2)、完整效果如下:
/**
* 启动插件
*/
public boolean start() {
Set<String> excludedMethodName = buildExcludedMethodName();
ConcurrentMap<String, AuthzHandler> authzMaps = new ConcurrentHashMap<String, AuthzHandler>();
//逐个访问所有注册的Controller,解析Controller及action上的所有Shiro注解。
//并依据这些注解,actionKey提前构建好权限检查处理器。
for (Routes routes : getRoutesList()) {
for (Routes.Route route : routes.getRouteItemList()) {
Class<? extends Controller> controllerClass = route.getControllerClass();
String controllerKey = route.getControllerKey();
// 获取Controller的所有Shiro注解。
List<Annotation> controllerAnnotations = getAuthzAnnotations(controllerClass);
// 逐个遍历方法。
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);
// ShiroKit.init(jdbcAuthzService, authzMaps, false);
/**
* 设定登录,登录成功,未授权等url地址
*/
ShiroKit.setLoginUrl(loginUrl);
ShiroKit.setSuccessUrl(successUrl);
ShiroKit.setUnauthorizedUrl(unauthorizedUrl);
return true;
}
private List<Routes> getRoutesList() {
List<Routes> routesList = Routes.getRoutesList();
List<Routes> ret = new ArrayList<Routes>(routesList.size() + 1);
ret.add(routes);
ret.addAll(routesList);
return ret;
}(3)、插件配置:
>web.xml配置支持shiro
<listener> <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class> </listener> <filter> <filter-name>ShiroFilter</filter-name> <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class> </filter> <filter-mapping> <filter-name>ShiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
>在yourConfig类声明一个route变量如:
private Routes route = null;
>在yourConfig找到configRoute接收路由参数方法配置如下:
@Override
public void configRoute(Routes me) {
this.route = me;
}>>补充:如果路由分片如:AdminRoutes、FrontRouts时,记得把 route声明为:private static Routes route = null;
然后在这样传值:
@Override
public void configRoute(Routes me) {
this.route = new AdminRoutes();
me.add(route); //管理后台路由
me.add(new FrontRoutes());//前台会员路由
}这样就可以把AdminRoutes()映射的路由给传过去了,当然有人再问题,如果两个路由块一起传进去怎么拿?声明一个集合变量如:private static List<Rotues> routeList = null;
然后在yourConfig的configRoute中是这样传值:
@Override
public void configRoute(Routes me) {
routesList.add(new AdminRoutes());
routesList.add(new FrontRoutes());
me.add(new AdminRoutes()); //管理后台路由
me.add(new FrontRoutes());//前台会员路由
}这时在ShiroPlugin再稍加修改下,把原插件传单个对象调整为传集合进去遍历下就可以了。
>在yourConfig找到configPlugin方法启动插件配置如下:
@Override
public void configPlugin(Plugins me) {
me.add(new ShiroPlugin(route));
}>在yourConfig找到configInterceptor方法开启shiro拦截器配置如下:
@Override
public void configInterceptor(Interceptors me) {
//启动Shiro拦截器
me.add(new ShiroInterceptor());
}>shrio.ini文件这个文件需要放在classpath目录下,如果是maven项目的话就放到resource下就好了
[main] #cookie sessionIdCookie=org.apache.shiro.web.servlet.SimpleCookie sessionIdCookie.name=wxhsopId sessionIdCookie.domain=.test.com sessionIdCookie.path=/ sessionIdCookie.maxAge=7 * 24 * 60 * 60 sessionIdCookie.httpOnly=true #session #sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO #sessionDAO = com.wxshop.common.auth.SessionDAO #sessionDAO.activeSessionsCacheName = shiro-activeSessionCache #sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager #sessionManager.sessionDAO = $sessionDAO #sessionManager.globalSessionTimeout = 1800000 #sessionManager.sessionIdCookieEnabled = false #securityManager.sessionManager = $sessionManager #CredentialsMatcher #credentialsMatcher=com.wxshop.common.auth.RetryLimitHashedCredentialsMatcher #credentialsMatcher.hashAlgorithmName=md5 #credentialsMatcher.hashIterations=2 #credentialsMatcher.storedCredentialsHexEncoded=true #realm myRealm = com.wxshop.common.auth.ShiroDbRealm #myRealm.credentialsMatcher=$credentialsMatcher securityManager.realms = $myRealm shiro.loginUrl=login [urls] /login = anon /index/img = anon /index/user/logout = anon /index = authc /index/** = authc /** = anon /druid/** = authc,roles[admin] /monitoring/** = authc,roles[admin]
>shiro.ini文件中myRealm =com.wxshop.comon.auth.ShiroDbRealm参考实现:
public class ShiroDbRealm extends AuthorizingRealm {
@Override
public void setCacheManager(CacheManager cacheManager) {
super.setCacheManager(cacheManager);
ShiroCache.setCacheManager(cacheManager);
}
/**
* 身份证认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
String username = usernamePasswordToken.getUsername();
Record user = UserService.me.login(username);
SimpleAuthenticationInfo authenticationInfo = null;
if (!sl.isEmpty(user)) {
String name = user.get("name");
Record shiroUser = new Record();
shiroUser.set("id", user.getInt("id"));
shiroUser.set("name", name);
authenticationInfo = new SimpleAuthenticationInfo(shiroUser, user.getStr("pass"),ByteSource.Util.bytes(user.getStr("salt")), getName());
// authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(name + user.get("salt")));
}
return authenticationInfo;
}
/**
* 权限认证
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
Record user = (Record) principalCollection.fromRealm(getName()).iterator().next();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
if (sl.isEmpty(user)) {
return info;
}
int id = user.get("id");
authRole(id, info);
return info;
}
/**
* 角色授权
* @param id
* @param info
*/
protected void authRole(int id, SimpleAuthorizationInfo info) {
List<Record> roleList = RoleService.me.byUid(id);
if (!sl.isEmpty(roleList)) {
for (Record role : roleList) {
info.addRole(role.get("name"));
authUrl(role.get("id"), info);
}
}
}
/**
* 资源授权
* @param id
* @param info
*/
protected void authUrl(int id, SimpleAuthorizationInfo info) {
List<Record> resourceList = ResourceService.me.byRid(id);
if (!sl.isEmpty(resourceList)) {
for (Record resource : resourceList) {
info.addStringPermission(resource.get("url"));
}
}
}
public void clearCacheAuth(Object principal) {
SimplePrincipalCollection info = new SimplePrincipalCollection(principal, getName());
clearCachedAuthenticationInfo(info);
}
public void clearAllCacheAuth() {
Cache<Object, AuthenticationInfo> cache = getAuthenticationCache();
if (null != cache) {
for (Object key : cache.keys()) {
cache.remove(key);
}
}
}>另外在不想一个个controler 手工@RequiresPermissions("/index/xx/xx")的话就创建一个全局拦截器,统一处理如:
/**
* 让 shiro基于url拦截
*
* 主要 数据库中也用url 保存权限
*/
public class UrlShiroInterceptor implements Interceptor{
private static ShiroExt ext = new ShiroExt();
/**
* 获取全部 需要控制的权限
*/
private static List<String> urls;
public static void updateUrls() {
urls = ResourceService.me.urls();
}
public void intercept(Invocation ai) {
if (urls == null) {
urls = ResourceService.me.urls();
}
String url = ai.getActionKey();
try {
if (url.contains("add")){
// LogService.me.add(ai.getController(), LogService..EVENT_ADD);
} else if (url.contains("delete")) {
// LogService.me.add(ai.getController(), Log.EVENT_DELETE);
} else if (url.contains("update")) {
// LogService.me.add(ai.getController(), Log.EVENT_UPDATE);
} else if (url.contains("index")) {
// LogService.me.add(ai.getController(), Log.EVENT_QUERY);
} else if (url.contains("goto")) {
}
if (urls.contains(url) && !ext.hasPermission(url)) {
ai.getController().renderError(403);
}
} catch (Exception e) {
ai.getController().renderError(403);
}
ai.invoke();
}
}2、即然JFinal 已具备Shiro扩展,怎么放过标签的应用的,如果没有标签的应用,改造成只能算是完成了一半,因为JFinal Template Engine模板引擎的极速特点,以下扩展拿来就可以实现JFinal Template Engine 的shrio标签扩展
(1)、标签代码:
public class ShiroTag {
/* The guest tag
*
* @return
*/
public boolean isGuest() {
return getSubject() == null || getSubject().getPrincipal() == null;
}
/**
* The user tag
*
* @return
*/
public boolean isUser() {
return getSubject() != null && getSubject().getPrincipal() != null;
}
/**
* The authenticated tag
*
* @return
*/
public boolean isAuthenticated() {
return getSubject() != null && getSubject().isAuthenticated();
}
public boolean isNotAuthenticated() {
return !isAuthenticated();
}
/**
* The principal tag
*
* @param map
* @return
*/
public String principal(Map map) {
String strValue = null;
if (getSubject() != null) {
// Get the principal to print out
Object principal;
String type = map != null ? (String) map.get("type") : null;
if (type == null) {
principal = getSubject().getPrincipal();
} else {
principal = getPrincipalFromClassName(type);
}
String property = map != null ? (String) map.get("property") : null;
// Get the string value of the principal
if (principal != null) {
if (property == null) {
strValue = principal.toString();
} else {
strValue = getPrincipalProperty(principal, property);
}
}
}
if (strValue != null) {
return strValue;
} else {
return null;
}
}
/**
* The hasRole tag
*
* @param roleName
* @return
*/
public boolean hasRole(String roleName) {
return getSubject() != null && getSubject().hasRole(roleName);
}
/**
* The lacksRole tag
*
* @param roleName
* @return
*/
public boolean lacksRole(String roleName) {
boolean hasRole = getSubject() != null
&& getSubject().hasRole(roleName);
return !hasRole;
}
/**
* The hasAnyRole tag
*
* @param roleNames
* @return
*/
public boolean hasAnyRole(String roleNames) {
boolean hasAnyRole = false;
Subject subject = getSubject();
if (subject != null) {
// Iterate through roles and check to see if the user has one of the
// roles
for (String role : roleNames.split(",")) {
if (subject.hasRole(role.trim())) {
hasAnyRole = true;
break;
}
}
}
return hasAnyRole;
}
/**
* The hasPermission tag
*
* @param p
* @return
*/
public boolean hasPermission(String p) {
return getSubject() != null && getSubject().isPermitted(p);
}
/**
* The lacksPermission tag
*
* @param p
* @return
*/
public boolean lacksPermission(String p) {
return !hasPermission(p);
}
@SuppressWarnings({"unchecked"})
private Object getPrincipalFromClassName(String type) {
Object principal = null;
try {
Class cls = Class.forName(type);
principal = getSubject().getPrincipals().oneByType(cls);
} catch (ClassNotFoundException e) {
}
return principal;
}
private String getPrincipalProperty(Object principal, String property) {
String strValue = null;
try {
BeanInfo bi = Introspector.getBeanInfo(principal.getClass());
// Loop through the properties to get the string value of the
// specified property
boolean foundProperty = false;
for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
if (pd.getName().equals(property)) {
Object value = pd.getReadMethod().invoke(principal,
(Object[]) null);
strValue = String.valueOf(value);
foundProperty = true;
break;
}
}
if (!foundProperty) {
final String message = "Property [" + property
+ "] not found in principal of type ["
+ principal.getClass().getName() + "]";
throw new RuntimeException(message);
}
} catch (Exception e) {
final String message = "Error reading property [" + property
+ "] from principal of type ["
+ principal.getClass().getName() + "]";
throw new RuntimeException(message, e);
}
return strValue;
}
protected Subject getSubject() {
return SecurityUtils.getSubject();
}
}(2)、在yourConfig找到configEngine方法配置如下:
@Override
public void configEngine(Engine me) {
me.addSharedObject("shiro", new ShiroTag());
}(3)、UI界面使用效果如:
#if(shiro.hasPermission("/home/resource/add"))
<a data-for="/home/resource/add" id="add" class="btn btn-small">
<i class="icon"></i>新增
</a>
#end以上还有其它标签就不一个个列出来了,自己以面例子调用就成了。