前言
本章讲解Shiro整合SSH时授权的相关知识
方法
1.准备工作
我们将使用自定义的realm来进行授权的管理,那么数据库表和实体类就需要自己来搭建了,我这里已经准备好了!
除了之前的用户表之外,我们还需要配置如下两个表,用户-角色表和角色-权限表
编写对应的实体类: UserRoles.java和RolesPers.java
编写好通过用户名获取角色信息和通过角色获取权限信息的业务方法
代码略
2.编写自定义realm的授权代码
package cn.edu.ccut.realm;
import cn.edu.ccut.bo.RolesPers;
import cn.edu.ccut.bo.User;
import cn.edu.ccut.bo.UserRoles;
import cn.edu.ccut.service.StudentService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @Auther:jwang
* @Date:2019/5/12
* @Description:cn.edu.ccut.realm
* @Version 1.0
**/
public class UserRealm extends AuthorizingRealm {
@Autowired
private StudentService studentService;
@Override
public String getName() {
return "UserRealm";
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) getAvailablePrincipal(principals);
List<UserRoles> roleNames = studentService.getUserRoles(username);
List<String> perNames = studentService.getRolesPers(roleNames);
//需要告知SimpleAuthorizationInfo我们这个用户拥有哪些角色和权限
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
for (UserRoles roleName : roleNames) {
info.addRole(roleName.getRoleName());
}
info.addStringPermissions(perNames);
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();
User user = studentService.getUserInfo(username);
String password = user == null?"":user.getPassword();
String password_salt = user == null?"":user.getPassword_salt();
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password.toCharArray(), ByteSource.Util.bytes(password_salt), getName());
return info;
}
}
自定义的realm务必要告诉shiro这个用户有哪些角色和权限。这点尤为重要!
3.测试程序
这个时候我们回过头来继续配置拦截器链:
我们配置http://localhost:8090/admin/list/student.jsp的访问必须要有permit1的权限才行!
在数据库中配置好用户zhangsan的角色和它的权限:
我们重新启动程序进行测试:
我们先直接访问http://localhost:8090/admin/list/student.jsp
发现其未认证直接跳转至登录界面!
我们进行登录 zhangsan/1234
再次进行访问,因为zhangsan有权限,所以访问成功了。
我们换个admin用户登录一下:
再次访问这个页面,由于其没有权限,自动跳转至无权限页面,也就是我们之前配置的页面。
注意:由于这种配置在xml中,一旦菜单或请求过多,那么将使得配置文件变的十分膨胀,更友好的方式是使用注解。
4.使用注解进行权限管理
我们需要在shiro配置文件中添加如下配置:
<!-- Shiro权限注解支持-->
<aop:config proxy-target-class="true"></aop:config>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
但是上面的配置方式我的注解没有生效,暂时不是很清楚。
没关系,我们采取下面的方式:
在spring-servlet.xml中做如下配置:
<!-- shiro注解支持 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.apache.shiro.authz.UnauthorizedException">/admin/list/refuse</prop>
</props>
</property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
注意:当没有访问权限的时候会抛出异常,所以这里进行了异常的捕获处理,将跳转至refuse页面。
shiro注解一般有以下几种:
@RequiresAuthentication
表示当前Subject已经通过login 进行了身份验证;即Subject. isAuthenticated()返回true。
@RequiresUser
表示当前Subject已经身份验证或者通过记住我登录的。
@RequiresGuest
表示当前Subject没有身份验证或通过记住我登录过,即是游客身份。
@RequiresRoles(value={“admin”, “user”}, logical= Logical.AND)
表示当前Subject需要角色admin 和user。
@RequiresRoles(value={“admin”})、@RequiresRoles({“admin“})
表示当前Subject需要角色admin
@RequiresPermissions (value={“user:a”, “user:b”}, logical= Logical.OR)
表示当前Subject需要权限user:a或user:b
shiro的注解一般加在controller的方法之上。
示例:登录需要有角色role1
我们知道zhangsan拥有该角色,我们首先使用zhangsan进行登录:
这里zhangsan顺利登陆了!
使用admin进行登录查看效果:
5.使用jsp标签进行权限验证
首先需要在jsp中引用shiro标签:
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
shiro中有以下jsp标签供我们使用:
(1)guest标签:用户没有身份验证时显示相应信息,即游客访问信息
(2)user标签:用户已经身份验证/记住我登录后显示相应的信息
(3)authenticated标签 :用户已经身份验证通过,即Subject.login登录成功,不是记住我登录的
(4)notAuthenticated标签 :用户已经身份验证通过,即没有调用Subject.login进行登录,包括记住我自动登录的也属于未进行身份验证
(5)principal标签 :显示用户身份信息,默认调用Subject.getPrincipal()获取,即Primary Principal
(6)lacksPermission标签 :如果当前Subject没有权限将显示body体内容
(7)hasRole标签 :如果当前Subject有角色将显示body体内容
(8)hasAnyRoles标签 :如果当前Subject有任意一个角色(或的关系)将显示body体内容
(9)lacksRole标签 :如果当前Subject没有角色将显示body体内容
(10)hasPermission标签 :如果当前Subject有权限将显示body体内容
示例:使用hasRole标签判定超链接:
拥有role1的用户才有身份认证菜单:
我们使用admin用户登录:
可以发现我们的admin因为没有role1角色而无法产生身份验证菜单。
接下来我们使用zhangsan登录:
可见,我们的标签验证成功!