项目里一直用的是 spring-security ,不得不说,spring-security 真是东西太多了,学习难度太大(可能我比较菜),这篇博客来总结一下折腾shiro的成果,分享给大家,强烈推荐shiro,真心简单 : )

引入依赖

<ol><li><span>&lt;dependency&gt;</span></li><li><span> </span><span>&lt;groupId&gt;</span><span>org.apache.shiro</span><span>&lt;/groupId&gt;</span></li><li><span> </span><span>&lt;artifactId&gt;</span><span>shiro-spring</span><span>&lt;/artifactId&gt;</span></li><li><span> </span><span>&lt;version&gt;</span><span>1.4.0</span><span>&lt;/version&gt;</span></li><li><span>&lt;/dependency&gt;</span></li></ol>

用户,角色,权限

就是经典的RBAC权限系统,下面简单给一下实体类字段

AdminUser.java

<ol><li><span>public</span><span> </span><span>class</span><span> </span><span>AdminUser</span><span> </span><span>implements</span><span> </span><span>Serializable</span><span> </span><span>{</span></li><li><span>&nbsp;</span></li><li><span> </span><span>private</span><span> </span><span>static</span><span> </span><span>final</span><span> </span><span>long</span><span> serialVersionUID </span><span>=</span><span> </span><span>8264158018518861440L</span><span>;</span></li><li><span> </span><span>private</span><span> </span><span>Integer</span><span> id</span><span>;</span></li><li><span> </span><span>private</span><span> </span><span>String</span><span> username</span><span>;</span></li><li><span> </span><span>private</span><span> </span><span>String</span><span> password</span><span>;</span></li><li><span> </span><span>private</span><span> </span><span>Integer</span><span> roleId</span><span>;</span></li><li><span> </span><span>// getter setter...</span></li><li><span>}</span></li></ol>

Role.java

<ol><li><span>public</span><span> </span><span>class</span><span> </span><span>Role</span><span> </span><span>implements</span><span> </span><span>Serializable</span><span> </span><span>{</span></li><li><span> </span><span>private</span><span> </span><span>static</span><span> </span><span>final</span><span> </span><span>long</span><span> serialVersionUID </span><span>=</span><span> </span><span>7824693669858106664L</span><span>;</span></li><li><span> </span><span>private</span><span> </span><span>Integer</span><span> id</span><span>;</span></li><li><span> </span><span>private</span><span> </span><span>String</span><span> name</span><span>;</span></li><li><span> </span><span>// getter setter...</span></li><li><span>}</span></li></ol>

Permission.java

<ol><li><span>public</span><span> </span><span>class</span><span> </span><span>Permission</span><span> </span><span>implements</span><span> </span><span>Serializable</span><span> </span><span>{</span></li><li><span> </span><span>private</span><span> </span><span>static</span><span> </span><span>final</span><span> </span><span>long</span><span> serialVersionUID </span><span>=</span><span> </span><span>-</span><span>2694960432845360318L</span><span>;</span></li><li><span> </span><span>private</span><span> </span><span>Integer</span><span> id</span><span>;</span></li><li><span> </span><span>private</span><span> </span><span>String</span><span> name</span><span>;</span></li><li><span> </span><span>private</span><span> </span><span>String</span><span> value</span><span>;</span></li><li><span> </span><span>// 权限的父节点的id</span></li><li><span> </span><span>private</span><span> </span><span>Integer</span><span> pid</span><span>;</span></li><li><span> </span><span>// getter setter...</span></li><li><span>}</span></li></ol>

自定义Realm

这货就是查询用户的信息然后放在shiro的个人用户对象的缓存里,shiro自己有一个session的对象(不是servlet里的session)作用就是后面用户发起请求的时候拿来判断有没有权限

另一个作用是查询一下用户的信息,将用户名,密码组装成一个 AuthenticationInfo 用于后面密码校验的

具体代码如下

MyShiroRealm.java

<ol><li><span>@Component</span></li><li><span>public</span><span> </span><span>class</span><span> </span><span>MyShiroRealm</span><span> </span><span>extends</span><span> </span><span>AuthorizingRealm</span><span> </span><span>{</span></li><li><span> </span><span>private</span><span> </span><span>Logger</span><span> log </span><span>=</span><span> </span><span>LoggerFactory</span><span>.</span><span>getLogger</span><span>(</span><span>MyShiroRealm</span><span>.</span><span>class</span><span>);</span></li><li><span> </span><span>@Autowired</span></li><li><span> </span><span>private</span><span> </span><span>AdminUserService</span><span> adminUserService</span><span>;</span></li><li><span> </span><span>@Autowired</span></li><li><span> </span><span>private</span><span> </span><span>RoleService</span><span> roleService</span><span>;</span></li><li><span> </span><span>@Autowired</span></li><li><span> </span><span>private</span><span> </span><span>PermissionService</span><span> permissionService</span><span>;</span></li><li><span> </span><span>// 用户权限配置</span></li><li><span> </span><span>@Override</span></li><li><span> </span><span>protected</span><span> </span><span>AuthorizationInfo</span><span> doGetAuthorizationInfo</span><span>(</span><span>PrincipalCollection</span><span> principals</span><span>)</span><span> </span><span>{</span></li><li><span> </span><span>//访问@RequirePermission注解的url时触发</span></li><li><span> </span><span>SimpleAuthorizationInfo</span><span> simpleAuthorizationInfo </span><span>=</span><span> </span><span>new</span><span> </span><span>SimpleAuthorizationInfo</span><span>();</span></li><li><span> </span><span>AdminUser</span><span> adminUser </span><span>=</span><span> adminUserService</span><span>.</span><span>selectByUsername</span><span>(</span><span>principals</span><span>.</span><span>toString</span><span>());</span></li><li><span> </span><span>//获得用户的角色,及权限进行绑定</span></li><li><span> </span><span>Role</span><span> role </span><span>=</span><span> roleService</span><span>.</span><span>selectById</span><span>(</span><span>adminUser</span><span>.</span><span>getRoleId</span><span>());</span></li><li><span> </span><span>// 其实这里也可以不要权限那个类了,直接用角色这个类来做鉴权,</span></li><li><span> </span><span>// 不过角色包含很多的权限,已经算是大家约定的了,所以下面还是查询权限然后放在AuthorizationInfo里</span></li><li><span> simpleAuthorizationInfo</span><span>.</span><span>addRole</span><span>(</span><span>role</span><span>.</span><span>getName</span><span>());</span></li><li><span> </span><span>// 查询权限</span></li><li><span> </span><span>List</span><span>&lt;</span><span>Permission</span><span>&gt;</span><span> permissions </span><span>=</span><span> permissionService</span><span>.</span><span>selectByRoleId</span><span>(</span><span>adminUser</span><span>.</span><span>getRoleId</span><span>());</span></li><li><span> </span><span>// 将权限具体值取出来组装成一个权限String的集合</span></li><li><span> </span><span>List</span><span>&lt;</span><span>String</span><span>&gt;</span><span> permissionValues </span><span>=</span><span> permissions</span><span>.</span><span>stream</span><span>().</span><span>map</span><span>(</span><span>Permission</span><span>::</span><span>getValue</span><span>).</span><span>collect</span><span>(</span><span>Collectors</span><span>.</span><span>toList</span><span>());</span></li><li><span> </span><span>// 将权限的String集合添加进AuthorizationInfo里,后面请求鉴权有用</span></li><li><span> simpleAuthorizationInfo</span><span>.</span><span>addStringPermissions</span><span>(</span><span>permissionValues</span><span>);</span></li><li><span> </span><span>return</span><span> simpleAuthorizationInfo</span><span>;</span></li><li><span> </span><span>}</span></li><li><span> </span><span>// 组装用户信息</span></li><li><span> </span><span>@Override</span></li><li><span> </span><span>protected</span><span> </span><span>AuthenticationInfo</span><span> doGetAuthenticationInfo</span><span>(</span><span>AuthenticationToken</span><span> token</span><span>)</span><span> </span><span>throws</span><span> </span><span>AuthenticationException</span><span> </span><span>{</span></li><li><span> </span><span>String</span><span> username </span><span>=</span><span> </span><span>(</span><span>String</span><span>)</span><span> token</span><span>.</span><span>getPrincipal</span><span>();</span></li><li><span> log</span><span>.</span><span>info</span><span>(</span><span>"用户:{} 正在登录..."</span><span>,</span><span> username</span><span>);</span></li><li><span> </span><span>AdminUser</span><span> adminUser </span><span>=</span><span> adminUserService</span><span>.</span><span>selectByUsername</span><span>(</span><span>username</span><span>);</span></li><li><span> </span><span>// 如果用户不存在,则抛出未知用户的异常</span></li><li><span> </span><span>if</span><span> </span><span>(</span><span>adminUser </span><span>==</span><span> </span><span>null</span><span>)</span><span> </span><span>throw</span><span> </span><span>new</span><span> </span><span>UnknownAccountException</span><span>();</span></li><li><span> </span><span>return</span><span> </span><span>new</span><span> </span><span>SimpleAuthenticationInfo</span><span>(</span><span>username</span><span>,</span><span> adminUser</span><span>.</span><span>getPassword</span><span>(),</span><span> getName</span><span>());</span></li><li><span> </span><span>}</span></li><li><span>}</span></li></ol>

实现密码校验

shiro内置了几个密码校验的类,有 Md5CredentialsMatcher Sha1CredentialsMatcher , 不过从1.1版本开始,都开始使用 HashedCredentialsMatcher 这个类了,通过配置加密规则来校验

它们都实现了一个接口 CredentialsMatcher 我这里也实现这个接口,实现一个自己的密码校验

说明一下,我这里用的加密方式是Spring-Security里的 BCryptPasswordEncoder 作的加密,之所以用它,是因为同一个密码被这货加密后,密文都不一样,下面是具体代码

<ol><li><span>public</span><span> </span><span>class</span><span> </span><span>MyCredentialsMatcher</span><span> </span><span>implements</span><span> </span><span>CredentialsMatcher</span><span> </span><span>{</span></li><li><span>&nbsp;</span></li><li><span> </span><span>@Override</span></li><li><span> </span><span>public</span><span> </span><span>boolean</span><span> doCredentialsMatch</span><span>(</span><span>AuthenticationToken</span><span> token</span><span>,</span><span> </span><span>AuthenticationInfo</span><span> info</span><span>)</span><span> </span><span>{</span></li><li><span> </span><span>// 大坑!!!!!!!!!!!!!!!!!!!</span></li><li><span> </span><span>// 明明token跟info两个对象的里的Credentials类型都是Object,断点看到的类型都是 char[]</span></li><li><span> </span><span>// 但是!!!!! token里转成String要先强转成 char[]</span></li><li><span> </span><span>// 而info里取Credentials就可以直接使用 String.valueOf() 转成String</span></li><li><span> </span><span>// 醉了。。</span></li><li><span> </span><span>String</span><span> rawPassword </span><span>=</span><span> </span><span>String</span><span>.</span><span>valueOf</span><span>((</span><span>char</span><span>[])</span><span> token</span><span>.</span><span>getCredentials</span><span>());</span></li><li><span> </span><span>String</span><span> encodedPassword </span><span>=</span><span> </span><span>String</span><span>.</span><span>valueOf</span><span>(</span><span>info</span><span>.</span><span>getCredentials</span><span>());</span></li><li><span> </span><span>return</span><span> </span><span>new</span><span> </span><span>BCryptPasswordEncoder</span><span>().</span><span>matches</span><span>(</span><span>rawPassword</span><span>,</span><span> encodedPassword</span><span>);</span></li><li><span> </span><span>}</span></li><li><span>}</span></li></ol>

配置shiro

因为项目是spring-boot开发的,shiro就用java代码配置,不用xml配置, 具体配置如下

<ol><li><span>@Configuration</span></li><li><span>public</span><span> </span><span>class</span><span> </span><span>ShiroConfig</span><span> </span><span>{</span></li><li><span> </span><span>private</span><span> </span><span>Logger</span><span> log </span><span>=</span><span> </span><span>LoggerFactory</span><span>.</span><span>getLogger</span><span>(</span><span>ShiroConfig</span><span>.</span><span>class</span><span>);</span></li><li><span> </span><span>@Autowired</span></li><li><span> </span><span>private</span><span> </span><span>MyShiroRealm</span><span> myShiroRealm</span><span>;</span></li><li><span> </span><span>@Bean</span></li><li><span> </span><span>public</span><span> </span><span>ShiroFilterFactoryBean</span><span> shiroFilter</span><span>(</span><span>SecurityManager</span><span> securityManager</span><span>)</span><span> </span><span>{</span></li><li><span> log</span><span>.</span><span>info</span><span>(</span><span>"开始配置shiroFilter..."</span><span>);</span></li><li><span> </span><span>ShiroFilterFactoryBean</span><span> shiroFilterFactoryBean </span><span>=</span><span> </span><span>new</span><span> </span><span>ShiroFilterFactoryBean</span><span>();</span></li><li><span> shiroFilterFactoryBean</span><span>.</span><span>setSecurityManager</span><span>(</span><span>securityManager</span><span>);</span></li><li><span> </span><span>//拦截器.</span></li><li><span> </span><span>Map</span><span>&lt;</span><span>String</span><span>,</span><span>String</span><span>&gt;</span><span> map </span><span>=</span><span> </span><span>new</span><span> </span><span>HashMap</span><span>&lt;&gt;();</span></li><li><span> </span><span>// 配置不会被拦截的链接 顺序判断 相关静态资源</span></li><li><span> map</span><span>.</span><span>put</span><span>(</span><span>"/static/**"</span><span>,</span><span> </span><span>"anon"</span><span>);</span></li><li><span> </span><span>//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了</span></li><li><span> map</span><span>.</span><span>put</span><span>(</span><span>"/admin/logout"</span><span>,</span><span> </span><span>"logout"</span><span>);</span></li><li><span> </span><span>//&lt;!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 --&gt;:这是一个坑呢,一不小心代码就不好使了;</span></li><li><span> </span><span>//&lt;!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问--&gt;</span></li><li><span> map</span><span>.</span><span>put</span><span>(</span><span>"/admin/**"</span><span>,</span><span> </span><span>"authc"</span><span>);</span></li><li><span> </span><span>// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面</span></li><li><span> shiroFilterFactoryBean</span><span>.</span><span>setLoginUrl</span><span>(</span><span>"/adminlogin"</span><span>);</span></li><li><span> </span><span>// 登录成功后要跳转的链接</span></li><li><span> shiroFilterFactoryBean</span><span>.</span><span>setSuccessUrl</span><span>(</span><span>"/admin/index"</span><span>);</span></li><li><span> </span><span>//未授权界面;</span></li><li><span> shiroFilterFactoryBean</span><span>.</span><span>setUnauthorizedUrl</span><span>(</span><span>"/error"</span><span>);</span></li><li><span> shiroFilterFactoryBean</span><span>.</span><span>setFilterChainDefinitionMap</span><span>(</span><span>map</span><span>);</span></li><li><span> </span><span>return</span><span> shiroFilterFactoryBean</span><span>;</span></li><li><span> </span><span>}</span></li><li><span> </span><span>// 配置加密方式</span></li><li><span> </span><span>// 配置了一下,这货就是验证不过,,改成手动验证算了,以后换加密方式也方便</span></li><li><span> </span><span>@Bean</span></li><li><span> </span><span>public</span><span> </span><span>MyCredentialsMatcher</span><span> myCredentialsMatcher</span><span>()</span><span> </span><span>{</span></li><li><span> </span><span>return</span><span> </span><span>new</span><span> </span><span>MyCredentialsMatcher</span><span>();</span></li><li><span> </span><span>}</span></li><li><span> </span><span>// 安全管理器配置</span></li><li><span> </span><span>@Bean</span></li><li><span> </span><span>public</span><span> </span><span>SecurityManager</span><span> securityManager</span><span>()</span><span> </span><span>{</span></li><li><span> </span><span>DefaultWebSecurityManager</span><span> securityManager </span><span>=</span><span> </span><span>new</span><span> </span><span>DefaultWebSecurityManager</span><span>();</span></li><li><span> myShiroRealm</span><span>.</span><span>setCredentialsMatcher</span><span>(</span><span>myCredentialsMatcher</span><span>());</span></li><li><span> securityManager</span><span>.</span><span>setRealm</span><span>(</span><span>myShiroRealm</span><span>);</span></li><li><span> </span><span>return</span><span> securityManager</span><span>;</span></li><li><span> </span><span>}</span></li><li><span>}</span></li></ol>

登录

都配置好了,就可以发起登录请求做测试了,一个简单的表单即可,写在Controller里就行

<ol><li><span>@PostMapping</span><span>(</span><span>"/adminlogin"</span><span>)</span></li><li><span>public</span><span> </span><span>String</span><span> adminLogin</span><span>(</span><span>String</span><span> username</span><span>,</span><span> </span><span>String</span><span> password</span><span>,</span></li><li><span>       </span><span>@RequestParam</span><span>(</span><span>defaultValue </span><span>=</span><span> </span><span>"0"</span><span>)</span><span> </span><span>Boolean</span><span> rememberMe</span><span>,</span></li><li><span>       </span><span>RedirectAttributes</span><span> redirectAttributes</span><span>)</span><span> </span><span>{</span></li><li><span> </span><span>try</span><span> </span><span>{</span></li><li><span> </span><span>// 添加用户认证信息</span></li><li><span> </span><span>Subject</span><span> subject </span><span>=</span><span> </span><span>SecurityUtils</span><span>.</span><span>getSubject</span><span>();</span></li><li><span> </span><span>if</span><span> </span><span>(!</span><span>subject</span><span>.</span><span>isAuthenticated</span><span>())</span><span> </span><span>{</span></li><li><span>  </span><span>UsernamePasswordToken</span><span> token </span><span>=</span><span> </span><span>new</span><span> </span><span>UsernamePasswordToken</span><span>(</span><span>username</span><span>,</span><span> password</span><span>,</span><span> rememberMe</span><span>);</span></li><li><span>  </span><span>//进行验证,这里可以捕获异常,然后返回对应信息</span></li><li><span>  subject</span><span>.</span><span>login</span><span>(</span><span>token</span><span>);</span></li><li><span> </span><span>}</span></li><li><span> </span><span>}</span><span> </span><span>catch</span><span> </span><span>(</span><span>AuthenticationException</span><span> e</span><span>)</span><span> </span><span>{</span></li><li><span> </span><span>// e.printStackTrace();</span></li><li><span> log</span><span>.</span><span>error</span><span>(</span><span>e</span><span>.</span><span>getMessage</span><span>());</span></li><li><span> redirectAttributes</span><span>.</span><span>addFlashAttribute</span><span>(</span><span>"error"</span><span>,</span><span> </span><span>"用户名或密码错误"</span><span>);</span></li><li><span> redirectAttributes</span><span>.</span><span>addFlashAttribute</span><span>(</span><span>"username"</span><span>,</span><span> username</span><span>);</span></li><li><span> </span><span>return</span><span> redirect</span><span>(</span><span>"/adminlogin"</span><span>);</span></li><li><span> </span><span>}</span></li><li><span> </span><span>return</span><span> redirect</span><span>(</span><span>"/admin/index"</span><span>);</span></li><li><span>}</span></li></ol>

从上面代码可以看出,记住我功能也直接都实现好了,只需要在组装 UsernamePasswordToken 的时候,将记住我字段传进去就可以了,值是 true, false, 如果是true,登录成功后,shiro会在本地写一个cookie

调用 subject.login(token); 方法后,它会去鉴权,期间会产生各种各样的异常,有以下几种,可以通过捕捉不同的异常然后提示页面不同的错误信息,相当的方便呀,有木有

  • AccountException 帐户异常
  • ConcurrentAccessException 这个好像是并发异常
  • CredentialsException 密码校验异常
  • DisabledAccountException 帐户被禁异常
  • ExcessiveAttemptsException 尝试登录次数过多异常
  • ExpiredCredentialsException 认证信息过期异常
  • IncorrectCredentialsException 密码不正确异常
  • LockedAccountException 帐户被锁定异常
  • UnknownAccountException 未知帐户异常
  • UnsupportedTokenException login(AuthenticationToken) 这个方法只能接收 AuthenticationToken 类的对象,如果传的是其它的类,就抛这个异常

上面这么多异常,shiro在处理登录的逻辑时,会自动的发出一些异常,当然你也可以手动去处理登录流程,然后根据不同的问题抛出不同的异常,手动处理的地方就在自己写的 MyShiroRealm 里的 doGetAuthenticationInfo() 方法里,我在上面代码里只处理了一个帐户不存在时抛出了一个 UnknownAccountException 的异常,其实还可以加更多其它的异常,这个要看个人系统的需求来定了

到这里已经可以正常的实现登录了,下面来说一些其它相关的功能的实现

自定freemarker标签

开发项目肯定要用到页面模板,我这里用的是 freemarker ,一个用户登录后,页面可能要根据用户的不同权限渲染不同的菜单,github上有个开源的库,也是可以用的,不过我觉得那个太麻烦了,就自己实现了一个,几行代码就能搞定

ShiroTag.java

<ol><li><span>@Component</span></li><li><span>public</span><span> </span><span>class</span><span> </span><span>ShiroTag</span><span> </span><span>{</span></li><li><span> </span><span>// 判断当前用户是否已经登录认证过</span></li><li><span> </span><span>public</span><span> </span><span>boolean</span><span> isAuthenticated</span><span>(){</span></li><li><span> </span><span>return</span><span> </span><span>SecurityUtils</span><span>.</span><span>getSubject</span><span>().</span><span>isAuthenticated</span><span>();</span></li><li><span> </span><span>}</span></li><li><span> </span><span>// 获取当前用户的用户名</span></li><li><span> </span><span>public</span><span> </span><span>String</span><span> getPrincipal</span><span>()</span><span> </span><span>{</span></li><li><span> </span><span>return</span><span> </span><span>(</span><span>String</span><span>)</span><span> </span><span>SecurityUtils</span><span>.</span><span>getSubject</span><span>().</span><span>getPrincipal</span><span>();</span></li><li><span> </span><span>}</span></li><li><span> </span><span>// 判断用户是否有 xx 角色</span></li><li><span> </span><span>public</span><span> </span><span>boolean</span><span> hasRole</span><span>(</span><span>String</span><span> name</span><span>)</span><span> </span><span>{</span></li><li><span> </span><span>return</span><span> </span><span>SecurityUtils</span><span>.</span><span>getSubject</span><span>().</span><span>hasRole</span><span>(</span><span>name</span><span>);</span></li><li><span> </span><span>}</span></li><li><span> </span><span>// 判断用户是否有 xx 权限</span></li><li><span> </span><span>public</span><span> </span><span>boolean</span><span> hasPermission</span><span>(</span><span>String</span><span> name</span><span>)</span><span> </span><span>{</span></li><li><span> </span><span>return</span><span> </span><span>!</span><span>StringUtils</span><span>.</span><span>isEmpty</span><span>(</span><span>name</span><span>)</span><span> </span><span>&amp;&amp;</span><span> </span><span>SecurityUtils</span><span>.</span><span>getSubject</span><span>().</span><span>isPermitted</span><span>(</span><span>name</span><span>);</span></li><li><span> </span><span>}</span></li><li><span>}</span></li></ol>

将这个类注册到freemarker的全局变量里

FreemarkerConfig.java

<ol><li><span>@Configuration</span></li><li><span>public</span><span> </span><span>class</span><span> </span><span>FreemarkerConfig</span><span> </span><span>{</span></li><li><span> </span><span>private</span><span> </span><span>Logger</span><span> log </span><span>=</span><span> </span><span>LoggerFactory</span><span>.</span><span>getLogger</span><span>(</span><span>FreeMarkerConfig</span><span>.</span><span>class</span><span>);</span></li><li><span> </span><span>@Autowired</span></li><li><span> </span><span>private</span><span> </span><span>ShiroTag</span><span> shiroTag</span><span>;</span></li><li><span> </span><span>@PostConstruct</span></li><li><span> </span><span>public</span><span> </span><span>void</span><span> setSharedVariable</span><span>()</span><span> </span><span>throws</span><span> </span><span>TemplateModelException</span><span> </span><span>{</span></li><li><span> </span><span>//注入全局配置到freemarker</span></li><li><span> log</span><span>.</span><span>info</span><span>(</span><span>"开始配置freemarker全局变量..."</span><span>);</span></li><li><span> </span><span>// shiro鉴权</span></li><li><span> configuration</span><span>.</span><span>setSharedVariable</span><span>(</span><span>"sec"</span><span>,</span><span> shiroTag</span><span>);</span></li><li><span> log</span><span>.</span><span>info</span><span>(</span><span>"freemarker自定义标签配置完成!"</span><span>);</span></li><li><span> </span><span>}</span></li><li><span>}</span></li></ol>

有了这些配置后,就可以在页面里使用了,具体用法如下

<ol><li><span>&lt;#if sec.hasPermission("topic:list")&gt;</span></li><li><span> &lt;li &lt;#if page_tab=='topic'&gt;class="active"&lt;/#if&gt;&gt;</span></li><li><span> </span><span>&lt;a</span><span> </span><span>href</span><span>=</span><span>"/admin/topic/list"</span><span> </span><span>rel</span><span>=</span><span>"external nofollow"</span><span> </span><span>&gt;</span></li><li><span>  </span><span>&lt;i</span><span> </span><span>class</span><span>=</span><span>"fa fa-list"</span><span>&gt;&lt;/i&gt;</span></li><li><span>  </span><span>&lt;span&gt;</span><span>话题列表</span><span>&lt;/span&gt;</span></li><li><span> </span><span>&lt;/a&gt;</span></li><li><span> </span><span>&lt;/li&gt;</span></li><li><span>&lt;/#if&gt;</span></li></ol>

加上这个后,在渲染页面的时候,就会根据当前用户是否有查看话题列表的权限,然后来渲染这个菜单

注解权限

有了上面freemarker标签判断是否有权限来渲染页面,这样做只能防君子,不能防小人,如果一个人知道后台的某个访问链接,但这个链接它是没有权限访问的,那他只要手动输入这个链接就还是可以访问的,所以这里还要在Controller层加一套防御,具体配置如下

在ShiroConfig里加上两个Bean

<ol><li><span>//加入注解的使用,不加入这个注解不生效</span></li><li><span>@Bean</span></li><li><span>public</span><span> </span><span>AuthorizationAttributeSourceAdvisor</span><span> authorizationAttributeSourceAdvisor</span><span>(</span><span>DefaultWebSecurityManager</span><span> securityManager</span><span>)</span><span> </span><span>{</span></li><li><span> </span><span>AuthorizationAttributeSourceAdvisor</span><span> authorizationAttributeSourceAdvisor </span><span>=</span><span> </span><span>new</span><span> </span><span>AuthorizationAttributeSourceAdvisor</span><span>();</span></li><li><span> authorizationAttributeSourceAdvisor</span><span>.</span><span>setSecurityManager</span><span>(</span><span>securityManager</span><span>);</span></li><li><span> </span><span>return</span><span> authorizationAttributeSourceAdvisor</span><span>;</span></li><li><span>}</span></li><li><span>@Bean</span></li><li><span>@ConditionalOnMissingBean</span></li><li><span>public</span><span> </span><span>DefaultAdvisorAutoProxyCreator</span><span> defaultAdvisorAutoProxyCreator</span><span>()</span><span> </span><span>{</span></li><li><span> </span><span>DefaultAdvisorAutoProxyCreator</span><span> defaultAAP </span><span>=</span><span> </span><span>new</span><span> </span><span>DefaultAdvisorAutoProxyCreator</span><span>();</span></li><li><span> defaultAAP</span><span>.</span><span>setProxyTargetClass</span><span>(</span><span>true</span><span>);</span></li><li><span> </span><span>return</span><span> defaultAAP</span><span>;</span></li><li><span>}</span></li></ol>

有了这两个Bean就可以用shiro的注解鉴权了,用法如下 @RequiresPermissions(“topic:list”)

<ol><li><span>@Controller</span></li><li><span>@RequestMapping</span><span>(</span><span>"/admin/topic"</span><span>)</span></li><li><span>public</span><span> </span><span>class</span><span> </span><span>TopicAdminController</span><span> </span><span>extends</span><span> </span><span>BaseAdminController</span><span> </span><span>{</span></li><li><span>&nbsp;</span></li><li><span> </span><span>@RequiresPermissions</span><span>(</span><span>"topic:list"</span><span>)</span></li><li><span> </span><span>@GetMapping</span><span>(</span><span>"/list"</span><span>)</span></li><li><span> </span><span>public</span><span> </span><span>String</span><span> list</span><span>()</span><span> </span><span>{</span></li><li><span> </span><span>// TODO</span></li><li><span> </span><span>return</span><span> </span><span>"admin/topic/list"</span><span>;</span></li><li><span> </span><span>}</span></li><li><span>}</span></li></ol>

shiro除了 @RequiresPermissions 注解外,还有其它几个鉴权的注解

  • @RequiresPermissions
  • @RequiresRoles
  • @RequiresUser
  • @RequiresGuest
  • @RequiresAuthentication

一般 @RequiresPermissions 就够用了

总结

spring-boot 集成 shiro 到这就结束了,是不是网上能找到的教程里最全的!相比 spring-security 要简单太多了,强烈推荐