框架模型原理
Subject: 主体,即“当前操作用户”。这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject。即一个抽象概念;所有 Subject 都绑定到 SecurityManager。
SecurityManager:安全管理器,用于管理Subject 拿到其给到Realm 在Realm中进行用户的认证与授权,可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,与Spring-Security的安全上下文相似。
Realm:领域,充当Shiro与应用程序的安全数据之间的“桥梁”或“连接器。与数据层的安全数据交互实现认证与授权。
代码实现
1.引入相关依赖
<!--rbac框架 shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.7.1</version>
<type>pom</type>
</dependency>
2.在applicationcContext.xml中加入
<!-- 注册自定义realm--> <bean id="rbacRealm" class="com.ssm.shiro.RBACRealm"> <!--定义的MD5加密算法--> <property name="credentialsMatcher" > <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <!--加密算法--> <property name="hashAlgorithmName" value="MD5"/> <!--加密次数--> <property name="hashIterations" value="1024"/> </bean> </property> </bean> <!--shiro rbac 基于角色的权限控制--> <!-- <bean class="org.apache.shiro.spring.web.ShiroFilterFactoryBean" id="shiroFilter"> 这是原生自带的ShiroFilterFactoryBean 下面是重新定义的--> <bean class="com.ssm.shiro.FilterFactoryBean" id="shiroFilter"> <!--注册安全管理器--> <property name="securityManager" ref="securityManager"/> <!-- 注入当前系统的登录页面 --> <property name="loginUrl" value="/page/login.jsp"/> <!-- 注入成功页面 --> <property name="successUrl" value="/page/main.jsp"/> <!-- 注入权限不足提示页面 --> <property name="unauthorizedUrl" value="/page/403.jsp"/> <!-- 注入URL拦截规则 --> <property name="filterChainDefinitions"> <value> /css/** = anon /img/** = anon /js/** = anon /plugins/** = anon /login.jsp* = anon /user/login.pp = anon /user/logout.pp =logout <!-- /product/* = perms["product"] /* = authc --> </value> </property> <property name="filters"> <map> <entry key="logout" value-ref="logoutFilter"/> </map> </property> </bean> <!--配置注销过滤器--> <bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter"> <property name="redirectUrl" value="/page/login.jsp"/> </bean> <!-- 配置 shiro 的核心组件:securityManager --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!-- 配置域realm,用户名,密码,角色都保存在域里:实现从数据库中获取用户信息, 需要我们自己创建一个类(实现Realm接口) --> <property name="realm" ref="rbacRealm"/> </bean>
3定义一个2中的Realm和FilterFacoryBean
package com.ssm.shiro; import com.ssm.dao.UsersMapper; import com.ssm.pojo.Permission; import com.ssm.pojo.Role; import com.ssm.pojo.Users; import com.ssm.pojo.UsersExample; 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.List; import java.util.Set; public class RBACRealm extends AuthorizingRealm { @Autowired private UsersMapper usersMapper; //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("开始"); SimpleAuthorizationInfo sa=new SimpleAuthorizationInfo(); Users primaryPrincipal =(Users) principalCollection.getPrimaryPrincipal(); Set<Role> roleSet = primaryPrincipal.getRoleSet(); if(roleSet!=null) { roleSet.forEach(role -> { sa.addRole(role.getRoleName()); Set<Permission> permissionSet = role.getPermissionSet(); permissionSet.forEach(permission -> { sa.addStringPermission(permission.getUrl()); }); }); } return sa; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //从Subject拿到用户登录的信息 UsernamePasswordToken upToken=(UsernamePasswordToken)authenticationToken; UsersExample usersExample= new UsersExample(); usersExample.createCriteria().andEmailEqualTo(upToken.getUsername()); List<Users> users = usersMapper.selectByExample(usersExample); //数据库中查到的信息 Users user=users.size()==1?users.get(0):null; if(user!=null){ //密码已加密 String pd=user.getPassword(); ByteSource bytes = ByteSource.Util.bytes(upToken.getUsername()); //返回这个对象让shiro帮我们完成认证 return new SimpleAuthenticationInfo(user,pd,bytes,this.getClass().getSimpleName()); } return null; } }
package com.ssm.shiro;
import com.ssm.dao.PermissionMapper;
import com.ssm.pojo.Permission;
import org.apache.shiro.config.Ini;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.web.config.IniFilterChainResolverFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FilterFactoryBean extends ShiroFilterFactoryBean {
@Autowired
private PermissionMapper permissionMapper;
@Override
public void setFilterChainDefinitions(String definitions) {
List<Permission> permissions = permissionMapper.selectByExample(null);
Map<String,String> otherChains= new HashMap<>();
permissions.forEach(permission->{
otherChains.put(permission.getUrl(),"perms["+permission.getPermissionCode()+"]");
});
//从配置文件加载权限配置
Ini ini = new Ini();
ini.load(definitions);
Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
if (CollectionUtils.isEmpty(section)) {
section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
}
section.putAll(otherChains);
//section.put("/**","authc");
section.put("/**","user");
setFilterChainDefinitionMap(section);
}
}
4在用户登录的接口中的实现
Subject subject = SecurityUtils.getSubject();//默认为未认证状态
UsernamePasswordToken token= new UsernamePasswordToken(users.getEmail(),users.getPassword());
if(users!=null&&("on").equals(remember)){
token.setRememberMe(true);
}
try {
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
return "login";
}catch (Exception e){
e.printStackTrace();
return "login";
}
//认证成功
Users user =(Users) subject.getPrincipal();
//查询用户身上携带的角色和权限
Users u1=userService.getWithRolePermission(user.getId());
user.setRoleSet(u1.getRoleSet());