目前从网上找到的绝大部分基本上都是基于spring或者使用spring的方式和shiro整合的方式方法,都需要在web.xml等配置 shiro的过滤器 来接管所有请求。我这边的主要需求就是想使用shiro的sessioin管理组件,权限校验组件等核心功能组件。
2.本着既然使用了jfinal,而且interceptor本身就很强大了,就不要再让别的去接管请求处理。去spring思维,去配置文件,不增加繁杂代码,低侵入性,灵活手工调用,易于插拔,28原则,使用核心功能。编写了一个小插件,来使用shiro的核心功能。如果后期想去掉使用系统自带功能,基本无成本。
3.插件的文件就2个类 ShiroPlugin,ShiroUtil.
直接上代码:
3.1).ShiroPlugin
根据jfinal插件规范编写
public class ShiroPlugin implements IPlugin{ /** * shiro的核心控制器,大脑 */ private DefaultSecurityManager securityManager = new DefaultWebSecurityManager(); /** * 设置session管理器 为 DefaultWebSessionManager(否则默认为ServletContainerSessionManager) */ private DefaultWebSessionManager sessionManager =new DefaultWebSessionManager(); /** * 单位毫秒 全局的 session过期时间,默认就是30分钟(1800000) */ private long globalSessionTimeout = 1800000L; /** * 与tomcat等web容器的默认的cookie中的键值 JSESSIONID冲突(ShiroHttpSession,中常量默认值JSESSIONID) * 会导致session的覆盖或丢失等问题 */ private final String SESSION_ID_NAME = "WEB_SESSIONID"; public ShiroPlugin(){ } public ShiroPlugin(Realm realm) { securityManager.setRealm(realm); } public ShiroPlugin(Collection<Realm> realms) { securityManager.setRealms(realms); } //如果通过ini文件来设置realm 那么需要配置 public ShiroPlugin(String iniPath) { IniRealm iniRealm = new IniRealm(iniPath); securityManager.setRealm(iniRealm); } private void init(){ Cookie cookie = new SimpleCookie(SESSION_ID_NAME); sessionManager.setSessionIdCookie(cookie); sessionManager.setGlobalSessionTimeout(globalSessionTimeout); //sessionManager.setCacheManager(cacheManager);缓存管理器 TODO securityManager.setSessionManager(sessionManager); //securityManager.setCacheManager(cacheManager);缓存管理器 TODO SecurityUtils.setSecurityManager(securityManager); } @Override public boolean start() { init(); return true; } @Override public boolean stop() { return true; } public DefaultSecurityManager getSecurityManager() { return securityManager; } public void setSecurityManager(DefaultSecurityManager securityManager) { this.securityManager = securityManager; } public DefaultWebSessionManager getSessionManager() { return sessionManager; } public void setSessionManager(DefaultWebSessionManager sessionManager) { this.sessionManager = sessionManager; } public long getGlobalSessionTimeout() { return globalSessionTimeout; } public void setGlobalSessionTimeout(long globalSessionTimeout) { this.globalSessionTimeout = globalSessionTimeout; } }
3.2).ShiroUtil
提供一些便捷的基本方法
public class ShiroUtil { public static Subject getSubject(){ return SecurityUtils.getSubject(); } public static Object getUser(){ return getSubject().getPrincipal(); } public static Session getSession(){ return SecurityUtils.getSubject().getSession(); } public static boolean hasRole(String roleIdentifier){ return getSubject().hasRole(roleIdentifier); } public static boolean hasAllRole(Collection<String> roleIdentifiers){ return getSubject().hasAllRoles(roleIdentifiers); } public static boolean hasAnyRole(Collection<String> roleIdentifiers){ boolean hasAnyRole = false; for (String role : roleIdentifiers) { if(hasRole(role)){ hasAnyRole = true; break; } } return hasAnyRole; } public static boolean hasPermit(String permitIdentifier){ return getSubject().isPermitted(permitIdentifier); } public static boolean hasAllPermit(String... permits){ return getSubject().isPermittedAll(permits); } public static boolean hasAnyPermit(String... permits){ boolean hasAnyPermit = false; for (String permit : permits) { if(hasPermit(permit)){ hasAnyPermit = true; break; } } return hasAnyPermit; } public static void refreshSessionLastAccessTime(){ try { getSession().touch(); } catch (Throwable t) { LogKit.error("session.touch() method invocation has failed.", t); } } public static SecurityManager getSecurityManager(){ return SecurityUtils.getSecurityManager(); } }
4. 使用方式:
4.1.启动配置
public void configPlugin(Plugins me) { ........ ........ ShiroPlugin sp = new ShiroPlugin(new 自定义的Realm对象()); me.add(sp); .......... .......... }
4.2. 定义一个interceptor
手动调用shiro进行校验(在一般spring项目中都是过滤器自动实现了这些校验过程,在spring和传统的在web.xml中配置filter等方式,会走很多层过滤器,然后根据配置的登录url,失败url,无权限url进行跳转。个人感觉很繁琐,不如在代码里面直接控制。
因为这里使用的JFinal,基本上项目中都是用自定义的若干interceptor来实现我们的拦截和过滤。这个拦截器仅使用了shiro的 验证是否登录和验证权限是否拥有,shiro其他强大功能未使用。
public class LoginShiroInterceptor implements Interceptor{ @Override public void intercept(Invocation inv) { Controller controller = inv.getController(); //对request和response进行包装 ShiroHttpServletRequest request = new ShiroHttpServletRequest(inv.getController().getRequest(), JFinal.me().getServletContext(), true); ShiroHttpServletResponse response = new ShiroHttpServletResponse(inv.getController().getResponse(),JFinal.me().getServletContext(),request); //手动创建 Subject 对象 Subject subject =(new WebSubject.Builder(request,response)).buildWebSubject(); //绑定 subject 并 刷新session最后访问时间,延长session过期时间 ThreadContext.bind(subject); ShiroUtil.refreshSessionLastAccessTime(); //核心逻辑 if(subject.isAuthenticated()){ LogKit.info("已经登陆"); if(ShiroUtil.hasRole("我是角色标识")){ //拥有该角色放行或者其他逻辑 } // 例如 通过 inv.getActionKey() 是否一致判断拥有该权限;subject拥有的角色和权限都已经在自定义的realm的 doGetAuthorizationInfo中定义好了。 if(ShiroUtil.hasPermit("我是权限标识")){ //拥有该权限放行或者其他逻辑 } }else{ LogKit.info("未登录"); //跳转登录或者其他逻辑处理 } } //TODO 对jwt等无状态服务进行验证的逻辑拦截 }
4.3提供一个 简单的 自定义realm 示例
public class AuthRealm extends AuthorizingRealm{ UserService userService = Enhancer.enhance(UserService.class); public AuthRealm() { } public AuthRealm(CredentialsMatcher credentialsMatcher) { super(credentialsMatcher); } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String userName = token.getPrincipal().toString(); String password = new String((char[])token.getCredentials()); SysUser user = userService.获取用户(username,password); // 账号不存在 if (user == null) { throw new UnknownAccountException("账号或密码不正确"); } // 账号锁定 if ("2".contentEquals(user.getAccountState())) { throw new LockedAccountException("账号已被锁定,请联系管理员"); } SimpleAuthenticationInfo authticInfo = new SimpleAuthenticationInfo(user, password, getName()); return authticInfo; } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SysUser user = (SysUser)ShiroUtil.getUser(); Set<String> rolesSet = userService.获取用户所有角色集合(user) Set<String> permsSet = userService.获取用户所有权限集合(user) SimpleAuthorizationInfo authorizinfo = new SimpleAuthorizationInfo(); authorizinfo.setRoles(rolesSet); authorizinfo.setStringPermissions(permsSet); return authorizinfo; }
}
4.5 提供一个 简单的登录controller 的 登录方法示例
@Log("登录") public void doLoginUseShiro(){ String userName = ... String password = ... String yzm = ... if(validateCaptcha("yzm")){//先验证验证码 UsernamePasswordToken token = new UsernamePasswordToken(userName,password); token.setRememberMe(true); ShiroHttpServletRequest request = new ShiroHttpServletRequest( getRequest(), JFinal.me().getServletContext(), true); ShiroHttpServletResponse response = new ShiroHttpServletResponse( getResponse(),JFinal.me().getServletContext(),request); Subject subject =(new WebSubject.Builder(request,response)).buildWebSubject(); ThreadContext.bind(subject); subject.login(token); this.renderJson(ret); }else{ this.renderJson(ret.setFail().set("msg", "验证码错误")); } }