做难事必有所得。——金一南
两个样例的基础
在我们学习了jfinal-shiro-jwt项目,以及jfinal-shiro-plugin项目后,我们对jfinal如何嵌入shiro有了一个较为清晰但是实际模糊的感觉,一切还是要自己动手实现来的实际,深入!
实际上jfinal-shiro-jwt项目已经给我们了一个很好的无状态样例,但是jfinal-shiro-plugin中有一个很好的地方,那就是他支持shiro中的注解。我们可以用类似下面的语句来控制接口权限:
1 2 3 4
| @RequiresRoles("abc") public void abc() { renderAppMsg("接口访问成功"); }
|
其中的语句:@RequiresRoles("abc")
表示需要当前登录人拥有角色abc
才可以访问当前接口。
接下来,我们逐步尝试探索将两者的优点结合起来,既能满足使用token进行无状态登录,又能很好地支持shiro注解。
自定义Realm
我们借鉴使用jfinal-shiro-jwt中的ShiroDbRealm:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| package com.xxx;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection;
public class ShiroDbRealm extends AuthorizingRealm{
@Override public boolean supports(AuthenticationToken token) { return token instanceof JWTToken; }
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); simpleAuthorizationInfo.addRole("admin"); simpleAuthorizationInfo.addStringPermission("all"); return simpleAuthorizationInfo; }
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException { String token = (String) auth.getCredentials(); String userName = JwtUtils.getUsername(token); if (userName == null || userName == "") { throw new AuthenticationException("token 校验失败"); } if(JwtUtils.verifyJwt(token, userName) == null) { throw new AuthenticationException("用户名或者密码错误"); } return new SimpleAuthenticationInfo(token, token, getName()); }
}
|
需要注意的时,ShiroDbRealm中的后两个方法,第一个返回的是AuthorizationInfo,是做权限授权的,第二个返回的是AuthenticationInfo,是做登录认证的。
Controller中的接口
我们在测试Controller中设置了三个接口,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| @Clear @Before(MyShiroInterceptor.class) public void noLogin() { renderAppError("请登录"); }
@Clear @Before(MyShiroInterceptor.class) public void testLogin() { String name = getPara("name"); String password = getPara("password"); if ("admin".equals(name)) { Map<String,String> map = new HashMap<>(); map.put("name", name); String token = JwtUtils.createJwt(map, new Date(System.currentTimeMillis()+360000)); renderAppJson(new Record().set("token", token)); } else { renderAppError("用户名与密码错误"); } }
@Clear @Before(MyShiroInterceptor.class) public void testAfterLogin() { renderAppMsg("我已经登录了"); }
|
这三个接口分别表示:
/login/noLogin: 登录验证失败,返回错误信息“请登录”,即在JWTFilter中使用代码配置的接口。
/login/testLogin: 登录接口,根据用户名与密码,获取登录token。
/login/testAfterLogin: 测试登录是否成功的接口。
我们尝试运行,发现无论怎么配置,包括在web.xml中配置始终无法像jfinal-shiro-jwt中一样,执行到JWTFilter。
我们查找项目与jfinal-shiro-jwt的区别:
所使用的jfinal版本不同,我们所使用的jfinal版本我4.8,而jfinal-shiro-jwt中使用的版本是3.6。因为其版本相比我们低,所以我们尝试将jwt项目中的版本升级到4.8,之后发现jwt仍然可以正常执行到过滤器中。
所使用的shiro版本不同,我们所使用的的shiro为1.9.0,而jwt中使用的是1.4.0,我们尝试给jwt升级shiro版本,之后发现其仍然可以正常运行。
除版本号外,我们发现两者的启动方式不同,jfinal 4.8中,我们的启动方式:
1
| UndertowServer.start(XXXConfig.class);
|
而jwt项目中的启动方式为jetty:
1
| JFinal.start("src/main/webapp", 8088, "/", 5);
|
将我们系统中的启动方式修改为jetty后~重要发现Filter中拦截到了请求信息。
总结
最终,我们发现其配置的Filter没有执行到,是因为undertow中配置filter不是通过web.xml配置。