Java Web框架入门(6):shiro
2016-12-01 00:00
477 查看
摘要: 本系列文章对 oschina 上较好的 jfinal、smart-framework 开源框架进行对比分析,比较两个框架的设计方法及优劣,从而对框架的一般设计有所了解。
上文的设计思路:
项目启动时,在 shiroPlugin中遍历所有 Action,将上述五种权限注解对应的处理器绑定起来,存入authzMaps<actionKey, authzHandler>
ShiroInterceptor 配置为 globalControllerInterceptors中的第一项,每次都先执行 actionKey对应的权限处理器,实现权限拦截
封装了上面的 ShiroPlugin,添加插件可提供相同的功能
重写一个 ShiroInterceptor,根据 URL进行拦截(数据库中也用 URL保存权限)
第二种方式算是一种新的思路,但要求数据库中的权限为 URL
另外作者也封装了 shiro的其他功能:最大尝试次数、最多同时在线人数等
可见通过以上简单五步,就集成了 shiro权限验证框架,具体代码参看 https://git.oschina.net/lan_cyl/smart-jfinal
功能:
提供 ShiroUrls类,类似 Routes,保存 URL及对应的权限拦截器。就是 shiro.ini的 urls部分
提取 shiro的所有权限过滤器,作为咱的拦截器
提供 ShiroPlugin完成 [main]部分功能,即配置 shiro的 realm
提供 ShiroInterceptor作为全局拦截器,根据 ShiroUrls执行权限验证
提供 ShiroRealm接口,让用户实现 获取用户、角色、权限的方法
实现一:封装并实现注解处理器
参看:https://my.oschina.net/myaniu/blog/137198上文的设计思路:
基于拦截器实现的 shiro权限注解处理
对 shiro提供的五种权限注解 RequiresPermissions、RequiresRoles、RequiresUser、RequiresGuest、RequiresAuthentication进行拦截,自定义处理方法即 authzHandler类项目启动时,在 shiroPlugin中遍历所有 Action,将上述五种权限注解对应的处理器绑定起来,存入authzMaps<actionKey, authzHandler>
ShiroInterceptor 配置为 globalControllerInterceptors中的第一项,每次都先执行 actionKey对应的权限处理器,实现权限拦截
封装权限验证方法
提供 ShiroMethod工具类,封装权限验证的方法:hasRole\lacksRole\hasAynRoles\hasAllRoles\hasPermission\lacksPermission\authenticated\notAuthenticated\user\guest\principal保留了 shiroFilter
所以在要做权限验证的 URL较多时,可以在 shiro.ini中配置 [urls]项(URL_Ant_Path_Expression = Path_Specific_Filter_Chain),首次适应原则小结
也算是比较完整的实现了,侧重点在基于注解的实现方式,但是对 shiro的其他方式也原封不动的保留了实现二:新的权限拦截约定
参看:http://git.oschina.net/jayqqaa12/JFinal_Authority封装了上面的 ShiroPlugin,添加插件可提供相同的功能
重写一个 ShiroInterceptor,根据 URL进行拦截(数据库中也用 URL保存权限)
第二种方式算是一种新的思路,但要求数据库中的权限为 URL
另外作者也封装了 shiro的其他功能:最大尝试次数、最多同时在线人数等
实现三:直接使用
如果不要基于注解的权限拦截方式,其实可以完全无侵入的在 shiro.ini中完成所有配置,在 web.xml开启过滤器就完了。顶多封装一个 shiro工具类,实现一个 realm就行了pom.xml 引入 shiro-web
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.3.2</version> </dependency>
web.xml启用 ShiroFilter
<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> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping>
添加 shiro.ini配置文件
[main] #比较密码的方法 credentialsMatcher = org.apache.shiro.authc.credential.HashedCredentialsMatcher credentialsMatcher.hashAlgorithmName=md5 credentialsMatcher.hashIterations=2 credentialsMatcher.storedCredentialsHexEncoded=true #realm,需要自己编写 shiroDbRealm = com.demo.login.ShiroDbRealm shiroDbRealm.credentialsMatcher = $credentialsMatcher securityManager.realms = $shiroDbRealm #[urls] /login/logout = logout /login/** = anon /index/** = authc /blog/** = authc
编写 realm类,完成 shiro与数据库的交互,实现具体的授权和认证方法
public class ShiroDbRealm extends AuthorizingRealm { /** * 认证回调函数,登录时调用,返回的 info 与录入的 token比对 */ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) { UsernamePasswordToken authcToken = (UsernamePasswordToken) token; String accountName = authcToken.getUsername(); SystemUser user = SystemUser.dao.findByEmail(accountName); if (null == user) throw new AuthenticationException("用户名或者密码错误"); if (user.isLocked()) throw new LockedAccountException("该用户已被锁定"); return new SimpleAuthenticationInfo(user.getEmail(), user.getPwd(), ByteSource.Util.bytes(user.getEmail() + user.getSalt2()), getName()); } /** * 授权查询回调函数,进行鉴权但缓存中无用户的授权信息时调用. */ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { //User user = (User) principals.fromRealm(getName()).iterator().next(); String username = (String) principals.fromRealm(getName()).iterator().next(); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); if (null == username) { return info; } SystemUser user = SystemUser.dao.findByEmail(username); if (null == user) { return info; } List<SystemRole> roles = SystemRole.dao.getRoles(user.getId()); if (CollectionUtils.isNotEmpty(roles)) { for (SystemRole role : roles) { // 保存角色的名称及角色的权限 info.addRole(role.getName()); info.addStringPermissions(SystemRes.dao.getResUrls(SystemRes.dao.getReses(role))); } } return info; } }
实现登陆功能
做完以上几步后,只用在 LoginController中编写 login、logout方法,在 SystemUser里编写encrypt方法对密码进行加密就行了,所有其他的操作都不需要 shiro了,只用在 shiro.ini 的 [urls]模块指定权限就行了public class LoginController extends Controller { private static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class); private static final String LOGIN_URL = "/login"; private static final String MAIN_URL = "/blog/1"; public void index() { if (SystemUser.dao.authenticated()) { // 调转到主页面 this.redirect(MAIN_URL); } else { // 显示登录页 this.createToken("loginToken"); this.render("login.html"); } } @Before(LoginValidator.class) public void login() { try { Subject subject = SecurityUtils.getSubject(); if (!subject.isAuthenticated()) { UsernamePasswordToken token = new UsernamePasswordToken(email, password); token.setRememberMe(true); subject.login(token); SystemUser user = findByEmail(email); subject.getSession(true).setAttribute(SESSION_USER_KEY, user); } // 调转到主页面 this.redirect(MAIN_URL); return; } catch (LockedAccountException e) { LOGGER.error(e.getMessage()); this.setAttr("msg", "账号已被锁定"); } catch (AuthenticationException e) { LOGGER.error(e.getMessage()); this.setAttr("msg", "用户名或者密码错误"); } catch (Exception e) { LOGGER.error(e.getMessage()); this.setAttr("msg", "系统异常"); } this.keepPara("username"); this.forwardAction(LOGIN_URL); } public void logout() { try { SystemUser.dao.logout(); this.redirect(LOGIN_URL); } catch (SessionException ise) { LOGGER.debug("Encountered session exception during logout. This can generally safely be ignored.", ise); } catch (Exception e) { LOGGER.debug("登出发生错误", e); } } }
注册用户的密码加密方法
public SystemUser encrypt() { String pwd = this.getPwd(); if (pwd != null) { String salt2 = new SecureRandomNumberGenerator().nextBytes().toHex(); SimpleHash hash = new SimpleHash("md5", pwd, getEmail() + salt2, 2); pwd = hash.toHex(); this.setPwd(pwd); this.setSalt2(salt2); } return this; }
可见通过以上简单五步,就集成了 shiro权限验证框架,具体代码参看 https://git.oschina.net/lan_cyl/smart-jfinal
实现四:深度集成
按照 jfinal的调性,就应该把 shiro给拆开,拿出 ShiroFilter这一条主线功能:
提供 ShiroUrls类,类似 Routes,保存 URL及对应的权限拦截器。就是 shiro.ini的 urls部分
提取 shiro的所有权限过滤器,作为咱的拦截器
提供 ShiroPlugin完成 [main]部分功能,即配置 shiro的 realm
提供 ShiroInterceptor作为全局拦截器,根据 ShiroUrls执行权限验证
提供 ShiroRealm接口,让用户实现 获取用户、角色、权限的方法
相关文章推荐
- Shiro安全框架入门
- shiro权限框架简单快速入门
- Java Web框架入门(3):AOP
- 如何通过Eclipse创建Web工程、正确配置Tomcat及Shiro安全框架入门
- Shiro安全框架入门篇(登录验证实例详解与源码)
- 开源权限框架shiro 入门
- Spring-SpringMVC-Shiro 入门到毁灭(第一步) [最基本框架搭建]
- 32、shiro框架入门3.授权
- 32、shiro 框架入门三
- 如何通过Eclipse创建Web工程、正确配置Tomcat及Shiro安全框架入门
- Shiro安全框架入门篇(登录验证实例详解与源码)
- 权限框架 - shiro 简单入门实例
- Shiro安全框架入门篇(登录验证实例详解与源码)
- 34、Shiro框架入门三,角色管理
- 关于shiro权限框架简单快速入门
- shiro权限框架简单快速入门
- Shiro安全框架入门篇(登录验证实例详解与源码)
- java安全框架-Shiro学习笔记(一)-入门小案例
- Shiro权限控制框架入门1:Shiro的认证流程以及基本概念介绍
- 第二章:Shiro入门——深入浅出学Shiro细粒度权限开发框架