记得第一次使用shiro是在才入行遇到公司的第一个框架,当时并不知道这是什么,或者说根本就没有安全框架的概念,在慢慢实践中,也对这个有了一定的了解,于是在网上找各种资料学习,了解。记得那时候比较有没的
记得第一次使用shiro是在才入行遇到公司的第一个框架,当时并不知道这是什么,或者说根本就没有安全框架的概念,在慢慢实践中,也对这个有了一定的了解,于是在网上找各种资料学习,了解。记得那时候比较有没的相关博客就是这个了,相信学习shiro的人很多都度过他的博客,内容也比较详细,示例也非常丰富。
开始使用shiro时,是与spring进行整合,可以看这里,当时没有实现太多功能,但是把一些外围的模块都已经实现,而且能够进行多realm匹配。在写这次博客前,也看了下renren-security,同样有很多可以借鉴的地方,所有既然看了这么多,那么自然要把功能做的完善一些了。
当然,此次的系统基础还是上次的springboot jpa搭建开发环境,我会在此基础上进行更新与扩展。
首先还是对shiro有一个初步的认识,当然这些认识都是收集与网络。
首先在学习这个框架时,都基本都会看到的图,那么我们需要了解的无非围绕着shiro里面这些核心的模块,具体是干什么,在什么时候,怎么去用,了解到这些之后,我们就可以按照自己的想法与设计,在适当的场合与环境下正确的使用该框架为我们提供的功能。
Authentication:身份认证/登录,验证用户是不是拥有相应的身份,该对象时对用户身份进行认证时,将相关信息存储的一个媒介,也是由开发者控制炎症逻辑的一个地方;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限,与上一个对象一起,都属于自定义Realm时需要由我们自己构建的;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的,在shiro是存在三种会话管理的工具,DefaultSessionManager,se环境会还管理;ServletContainerSessionManager,web环境,由web容器管理;DefaultWebSessionManager,支持以上两种,支持自定义会话管理。
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储,在jar包中包含了大量操作密码的工具类;
Web Support:Web支持,可以非常容易的集成到Web环境,shiro本身不依赖于web环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
更详细的信息可参考这个博客。
在进行编码工作前,还是需要了解一些其他比较重要的 概念,我们知道,shiro的核心就是认证和鉴权,那么实现原理无非是通过servlet的Filter来完成的。其实过滤器是分为两类,一类是完成用户的身份与凭证验证,也就是用户名密码验证,保证能够登录系统,另一类则是权限验证的过滤器,主要是对接口、数据的权限校验。
其内置过滤器有如下这些:
anon:例子/admins/**=anon 没有参数,表示可以匿名使用。
authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数
roles(角色):例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
perms(权限):例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。
port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString
是你访问的url里的?后面的参数。
authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证
ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
当然我们还可以自定义一些。具体的说明可以参考这里。
那么整合一个shiro框架我们具体需要添加哪些配置呢?如果和springmvc整合过的应该会比较清楚。当然,在shiro-spring这个包中,ShiroWebConfiguration、ShiroWebFilterConfiguration、ShiroAnnotationProcessorConfiguration已经有所有的内容,当然有一些是需要我们根据需要去修改的,毕竟默认的不一定能够满足我们的需要。
shiro基本可以当个黑盒使用,因为如何进行认证,如何进行全校校验已经由框架完成,留给开发者做的就是构建认证与鉴权需要的对象,而这些对象都是以realm的形式存在,我们只需要注入自定义的realm即可,简单的只用继承AuthorizingRealm这个类,实现抽象方法,doGetAuthenticationInfo为认证用的,我们需要构建认证对象,具体的逻辑由我们实现,doGetAuthorizationInfo为鉴权方法,当然是组织鉴权对象的方法。关于shiro,网上有太多太多的文章可以去参考,当然核心内容其实都是一样的。
说了这么多,是时候实际操作了,继续以之前的项目为基础,添加了一个security模块,同时将登入与主页路径进行了修改,因为是直接访问html,所以为了减去views这个路径,同时js也做了少量的修改:
核心其实都在security这包里,可以看到,内容其实不多,因为目前只是完成了认证,即用户登录的校验:
关于登录,有两种方式取实现:
1、自定义一个登录请求,自己完成token的定义与组织,同时完成login操作。这个时候我们会将/login.html过滤指向anon,同时登录行为请求也要设置成anon,本次就是通过这种方式实现:
@PostMapping("/dologin")public Message login(HttpServletRequest request){ String username = request.getParameter("username"); String password = request.getParameter("password"); String rememberMe = request.getParameter("rememberMe"); Subject subject = SecurityUtils.getSubject(); AuthenticationToken toker = new UsernamePasswordToken(username,password,rememberMe); Message message = new Message(Message.SUCCESS_CODE,username); try { boolean reLogin = true;//重新登录 if(subject.isAuthenticated()){//已经登录如果再次登录 if(reLogin){ subject.logout(); }else{ return message; } } subject.login(toker); } catch (AuthenticationException e) { e.printStackTrace(); message.setCode(Message.FAILURE_CODE); message.setInfo(loginFailure(e)); } return message;}
2、将登录页/login.html过滤器设置为authc(FormAuthenticationFilter),其实就是shiro专门为form表单提交的验证,这样需要我们在一定程度读上进行扩展,比如ajax提交时登录成功或失败的逻辑。但是要注意的是,提交的请求必须与登录页面地址一致,同时为POST类型。这个下次会用到,到时候可以看到具体实现。
然后看看realm:
public class GenericRealm extends AuthorizingRealm { @Autowired(required = false) private PrincipalService principalService; /** * 认证 * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { Object principal = token.getPrincipal(); if(principalService!=null){ IUser user = principalService.getUser(principal.toString()); if(user!=null){ AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),""); return authenticationInfo; } } return null; } /** * 鉴权 * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { return null; }}
其实很简单,毕竟是构建对象的过程,所以没有太多的东西,所以关于与shiro的整合目前完成认证就这些东西,但是为了实现一个比较完整的认证授权功能,还有很多需要我们去了解与修改的,将会在下次进行完善。
在此次整合的过程中,也发现一个问题 ,以前和spring整合时,自定义的shiro filter是交由spring管理的,但是和springboot整合时,如果将filter交由spring管理则会出现异常,主要是由于在进行普通请求时也会进入该filter,但是我们要知道,shiro的核心是securityManager,很可能在一般的请求中还没有这个对象,自然就会出现问题,所以在个shiiro配置自定义过滤器时,直接通过new的方式添加即可。
同样,本次的代码在这里。
本次项目花费的时间比较长,当然也不可能完成一个成形的项目,问题自然也就存在,但是毕竟是学习,存在一些遗留问题是必要的。目前该项目主要完成了简单的管理功能,如需要添加其他模块也有了一定的参考。由于后期有一些修改,所以之前页面有些写法可能还没来的及更新。
目前学习注重的是广度,因此基本每次进行了交叉方式来构建项目,比如这次用了springboot+jpa+shiro+layui,但是下次可能就不会用 相同的技术了,这样也是为了能快速对各种技术做个了解,并且能够快速上手。
下次将会通过springboot+springsecurity+mybatisplus+vue来搭建项目。