一.权限的几个定义
subject:主体,表示系统的操作用户
资源:作为开发者,主要考虑的是细粒度的资源,也就是对于系统的界面点击权限;例如查看列表的按钮
角色:不同的角色有不同的权限;同样,不同的用户有可能多个角色;这就相当于有多对多的关系
设计数据库时,我们会采用admin管理员表,role角色表,permission权限表;以及中间多对多关系admin-role,role-permiss表等。
二.整合开发
1.在web.xml文件中添加shiroFilter拦截器
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.springmvc的配置文件
<!-- 启用shrio授权注解拦截方式 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 装配 securityManager -->
<property name="securityManager" ref="securityManager"/>
<!-- 配置登陆页面 -->
<property name="loginUrl" value="/common/welcome.htm"/>
<!-- 登陆成功后的一面 -->
<property name="successUrl" value="/common/home.htm"/>
<property name="unauthorizedUrl" value="/common/unauthorized.htm"/>
<!-- 具体配置需要拦截哪些 URL, 以及访问对应的 URL 时使用 Shiro 的什么 Filter 进行拦截. -->
<property name="filterChainDefinitions">
<value>
<!--/common/login.htm=anon-->
/common/welcome.htm=anon
/common/login.htm=anon
/image/** = anon
/common/index.htm=perms[activity:list]
/survey/question_analysis.htm=perms[survey:list]
/message/edit_common_meesage.htm=perms[comsg:add]
/message/edit_ori_meesage.htm=perms[orimsg:add]
/post/manager_post_list.htm=perms[post:list]
/post/manager_post_list.htm=perms[post:list]
/post/manager_post_detail.htm=perms[post:detail]
/register/count_register_user.htm=perms[registeruser:check]
</value>
</property>
</bean>
<bean id="myRealm" class="com.syezon.chuda.manager.realm.MyRealm">
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="SHA1"></property>
<property name="hashIterations" value="1024"></property>
</bean>
</property>
</bean>
<!-- 配置 Shiro 的 SecurityManager Bean. -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"/>
</bean>
<!-- 配置 Bean 后置处理器: 会自动的调用和 Spring 整合后各个组件的生命周期方法. -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
容器启动以后会查找配置文件下面的id为shiroFilter的拦截器;与上面的web.xmlwen配置的名称必须相同,阅读过源码的应该知道具体是根据targetName进行匹配的。
对于登陆界面,登陆成功,未授权的配置在上面注释已经标记出来,至于我的为什么htm;因为公司使用VM模板驱动,这是接口的后缀,当然你也可以配置成html界面。
filterChainDefinitions是拦截链,anon表示不需要登陆就可以访问的界面,比如你的登陆界面,这里的配置顺序很有讲究,如果顺序不对,会导致配置不生效的情况。
relam可以说是权限框架的核心了,一般我们通过debug调试,当通过subject.login()方法登陆时,会发现会进入到自定义relam的doGetAuthenticationInfo()方法认证。返回info对象
3.自定义relam
public class MyRealm extends AuthorizingRealm {
@Resource
IAdminDao<Admin> adminDao;
@Resource
IAdminRoleDao<AdminRole> adminRoleDao;
@Resource
IRoleDao<Role> roleDao;
@Resource
IPermissionDao<Permission> permissionDao;
@Resource
IRolePermissionDao<RolePermission> rolePermissionDao;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
// Object credentials = null;
Map<String,Object> adminMap = new HashMap<>();
adminMap.put("username", username);
Admin admin = adminDao.getOneObject(adminMap);
if (null != admin){
ByteSource credentialsSalt = ByteSource.Util.bytes(username);
return new SimpleAuthenticationInfo(username, admin.getPassword(), credentialsSalt, getName());
}
return null;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
Object principal = principalCollection.getPrimaryPrincipal();
Set<String> permissions = new HashSet<>();
Map<String,Object> adminMap = new HashMap<>();
adminMap.put("username", principal);
Admin admin = adminDao.getOneObject(adminMap);
if (null != admin){
Map<String,Object> adminRoleMap = new HashMap<>();
adminRoleMap.put("adminid", admin.getId());
List<AdminRole> adminRoleList = adminRoleDao.select(adminRoleMap);
if (null != adminRoleList && adminRoleList.size() > 0){
for (AdminRole adminRole : adminRoleList){
Long roleid = adminRole.getRoleid();
Map<String,Object> rolePermissionMap = new HashMap<>();
rolePermissionMap.put("roleid", roleid);
List<RolePermission> rolePermissions = rolePermissionDao.select(rolePermissionMap);
if (null != rolePermissions && rolePermissions.size() > 0){
for (RolePermission rolePermission : rolePermissions){
Long permissionid = rolePermission.getPermissionid();
Map<String,Object> permissionMap = new HashMap<>();
permissionMap.put("id", permissionid);
Permission permission = permissionDao.getOneObject(permissionMap);
if (null != permission){
permissions.add(permission.getOperation());
}
}
}
}
}
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(permissions);
return info;
}
我们通过继承AuthorizingRealm方法重写认证和授权两个方法,里面具体写我们的业务逻辑。
最终返回
ByteSource credentialsSalt = ByteSource.Util.bytes(username);
return new SimpleAuthenticationInfo(username, admin.getPassword(), credentialsSalt, getName());
这里主要用到了密码加密,在上面springmvc的配置文件中,我们用到credentialsMatcher,匹配器;通过对于原密码的1024次SHA1加密,最终结果和数据库密码(已加密)比对,如果相同,表示登陆成功。
控制层的登陆接口
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
}catch (UnknownAccountException ue){
model.addAttribute("msg", "账号不存在");
return "/common/login";
}catch (IncorrectCredentialsException ie){
model.addAttribute("msg", "用户名或者密码不正确");
return "/common/login";
}catch (AuthenticationException ae){
model.addAttribute("msg", "未知错误");
return "/common/login";
}
shiro对于密码比对不正确这种情况会通过异常抛出;
说完登陆认证接下来说授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(permissions);
return info;
在自定义relam中通过集合将权限进行整合,返回一个info对象给shiro框架
上面springmvc的配置文件中已经配置了访问资源所需权限,如下
/post/manager_post_detail.htm=perms[post:detail]
也可以通过subject.isPermitted("activity:delete")
这种编码实现认证。
至于注解,与以上结果相似,主要还是理解shiro权限控制的过程