目前从网上找到的绝大部分基本上都是基于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", "验证码错误"));
}
}