- Shiro三个核心组件
1.1 Subject
Subject:即“当前操作用发户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
1.2 SecurityManager
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
1.3 Realm
Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
2.Shiro相关类介绍
- Authentication:身份认证/登录(账号密码验证)。
- Authorization:授权,即角色或者权限验证。
- Session Manager:会话管理,用户登录后的session相关管理。
- Cryptography:加密,密码加密等。
- Web Support:Web支持,集成Web环境。
- Caching:缓存,用户信息、角色、权限等缓存到如redis等缓存中。
- Concurrency:多线程并发验证,在一个线程中开启另一个线程,可以把权限自动传播过去。
- Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。
- Remember Me:记住我,登录后,下次再来的话不用登录了。
3.shiro整合springboot代码
3.1先创建我们的三个实体
userInfo用户实体
@ApiModel(value="UserInfo对象", description="")
@Data
public class UserInfo implements Serializable {
private static final long serialVersionUID=1L;
public UserInfo(Integer id, String userName, String password, List<RoleInfo> roleInfoList) {
this.id = id;
this.userName = userName;
this.password = password;
this.roleInfoList = roleInfoList;
}
public UserInfo() {
}
private Integer id;
@ApiModelProperty(value = "用户名")
private String userName;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "角色集合")
private List<RoleInfo> roleInfoList;
}
@ApiModel(value="RoleInfo对象", description="")
@Data
public class RoleInfo implements Serializable {
private static final long serialVersionUID=1L;
public RoleInfo(Integer id, String roleName, List<AuthorityInfo> authorityInfoList) {
this.id = id;
this.roleName = roleName;
this.authorityInfoList = authorityInfoList;
}
public RoleInfo() {
}
private Integer id;
@ApiModelProperty(value = "角色名")
private String roleName;
@ApiModelProperty(value = "角色所拥有的权限")
private List<AuthorityInfo> authorityInfoList;
}
@ApiModel(value="AuthorityInfo对象", description="")
@Data
public class AuthorityInfo implements Serializable {
private static final long serialVersionUID=1L;
public AuthorityInfo(Integer id, String authorityName) {
this.id = id;
this.authorityName = authorityName;
}
public AuthorityInfo() {
}
private Integer id;
@ApiModelProperty(value = "权限名")
private String authorityName;
}
3.2数据操作
由于没有连接数据库,
设置了两个用户(admin,zhangsan)
两个权限(add,query)
两个角色(admin,ordinary)
private UserInfo simulationGetInfo(String userName) {
// 设置两个权限 一个add 一个query
AuthorityInfo authorityInfoAdd = new AuthorityInfo(1,"add");
AuthorityInfo authorityInfoQuery = new AuthorityInfo(2,"query");
// 查询权限集合
List<AuthorityInfo> queryList = Lists.newArrayList();
queryList.add(authorityInfoQuery);
// 所有权限集合
List<AuthorityInfo> allList = Lists.newArrayList();
allList.add(authorityInfoAdd);
allList.add(authorityInfoQuery);
// 设置两个角色 一个管理员 一个普通用户
RoleInfo adminRole = new RoleInfo(1,"admin",allList);
RoleInfo ordinaryRole = new RoleInfo(2,"ordinary",queryList);
// 设置两个用户
UserInfo adminUser = new UserInfo(1,"admin","123456", Collections.singletonList(adminRole));
UserInfo zhangsanUser = new UserInfo(2,"zhangsan","123456",Collections.singletonList(ordinaryRole));
Map<String,UserInfo> map = Maps.newHashMap();
map.put(adminUser.getUserName(),adminUser);
map.put(zhangsanUser.getUserName(),zhangsanUser);
return map.get(userName);
}
配置我们的核心Realm
public class CustomerRealm extends AuthorizingRealm {
@Autowired
private ISysLoginService iSysLoginService;
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 获得用户登录名
UserInfo userInfo = iSysLoginService.getUserInfo(principalCollection.getPrimaryPrincipal().toString());
//添加角色和权限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for (RoleInfo roleInfo : userInfo.getRoleInfoList()) {
simpleAuthorizationInfo.addRole(roleInfo.getRoleName());
for (AuthorityInfo authorityInfo : roleInfo.getAuthorityInfoList()) {
simpleAuthorizationInfo.addStringPermission(authorityInfo.getAuthorityName());
}
}
return simpleAuthorizationInfo;
}
/**
* 认证 用户登录
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 加这一步的目的是在请求的时候会先进认证,然后在到请求
if (authenticationToken.getPrincipal() == null) {
return null;
}
//获取用户信息
UserInfo userInfo = iSysLoginService.getUserInfo(authenticationToken.getPrincipal().toString());
if (userInfo == null) {
//这里返回后会报出对应异常
return null;
} else {
//这里验证authenticationToken和simpleAuthenticationInfo的信息
return new SimpleAuthenticationInfo(userInfo.getUserName(), userInfo.getPassword(), getName());
}
}
}
shiro配置
@Configuration
public class ShiroConfig {
/**
* 该配置可解决shiro注解无法被映射问题
*
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
/**
* 将自己的验证方式加入容器
*
* @param
* @return: com.dpx.shiro.handle.CustomerRealm
* @author:
* @date: 2020/4/16
*/
@Bean
public CustomerRealm myShiroRealm() {
CustomerRealm customRealm = new CustomerRealm();
return customRealm;
}
/**
* 权限管理,核心,配置主要是Realm的管理认证
*
* @param
* @return: org.apache.shiro.mgt.SecurityManager
* @author:
* @date: 2020/4/16
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
/**
* Filter工厂,设置对应的过滤条件和跳转条件
*
* @param securityManager
* @return: org.apache.shiro.spring.web.ShiroFilterFactoryBean
* @author:
* @date: 2020/4/16
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> map = new HashMap<>();
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* anon: 无需认证(登录)可以访问
* authc: 必须认证才可以访问
* user: 如果使用rememberMe的功能可以直接访问
* perms: 该资源必须得到资源权限才可以访问
* role: 该资源必须得到角色权限才可以访问
*/
map.put("/login", "anon");
map.put("/add", "authc");
//登录
shiroFilterFactoryBean.setLoginUrl("/login");
//错误页面,认证不通过跳转
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
/**
* 注解访问授权动态拦截,不然不会执行doGetAuthenticationInfo
*
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
我们的两个入口
@RestController
public class SysLoginController {
@RequestMapping(value = "/login")
public String login(@RequestBody UserInfo userInfo) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken passwordToken = new UsernamePasswordToken(userInfo.getUserName(), userInfo.getPassword());
try {
subject.login(passwordToken);
} catch (AuthenticationException e) {
return "账号密码错误";
} catch (AuthorizationException e) {
return "没有权限";
}
return "登录成功";
}
@RequiresRoles("admin")
@RequiresPermissions("add")
@RequestMapping(value = "/add")
public String add() {
return "添加信息";
}
}
简单配置一个全局异常处理
@ControllerAdvice
public class ShiroException {
@ExceptionHandler
@ResponseBody
public String noAuthorityHandler(AuthorizationException e){
return "没有该权限";
}
}
4.测试一下结果
用我们的admin账号登录
再来请求add接口
用zhangsan普通账号登录
接下来访问add接口