shiro介绍
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
Apache Shiro 体系结构
1、 Authentication 认证 ---- 用户登录
2、 Authorization 授权 — 用户具有哪些权限
3、 Cryptography 安全数据加密
4、 Session Management 会话管理
5、 Web Integration web系统集成
6、 Interations 集成其它应用,spring、缓存框架
Shiro的核心API
- Subject: 用户主体(把操作交给SecurityManager)
- SecurityManager:安全管理器(关联Realm)
- Realm:Shiro连接数据的桥梁
三个核心api的关系是:
sbuject ==》 SecurityManager ==》 Realm
初次使用shiro
导入shiro与spring整合依赖
<!-- shiro与spring整合依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
创建shiro配置文件
具体的代码如下:
shiro配置文件
package edu.cjdx.shiro.config;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class shiroConfig {
/**
*创建ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager")DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
Map<String,String> filterMap = new LinkedHashMap<String,String>();
//设置放行,注意放行需要在设置权限之前
filterMap.put("/HC/test","anon");
filterMap.put("/HC/login","anon");
//授权过滤器
//注意:当授权拦截后,shiro会自动跳转到未授权的页面
filterMap.put("/HC/add","perms[user:add]");
filterMap.put("/HC/update","perms[user:update]");
//设置认证才可访问 /*要放在filterMap最下面
filterMap.put("/HC/*","authc");
//设置跳转登录页面
shiroFilterFactoryBean.setLoginUrl("/HC/toLogin");
//设置未授权提示页面
shiroFilterFactoryBean.setUnauthorizedUrl("/HC/noAuth");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
/**
*创建DefaultWebSecurityManager
*/
@Bean(name = "defaultWebSecurityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userRealm);
return defaultWebSecurityManager;
}
/**
*创建Realm
*/
@Bean(name = "userRealm")
public UserRealm getRealm(){
return new UserRealm();
}
}
Realm代码
要注意自定义的Realm方法必需要继承AuthorizingRealm类
package edu.cjdx.shiro.config;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import edu.cjdx.shiro.entity.Perms;
import edu.cjdx.shiro.entity.User;
import edu.cjdx.shiro.service.impl.PermsServiceImpl;
import edu.cjdx.shiro.service.impl.UserServiceImpl;
import org.apache.shiro.SecurityUtils;
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.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
* 自定义Realm
* Authorizing:授权
*/
public class UserRealm extends AuthorizingRealm {
@Autowired
UserServiceImpl userService;
@Autowired
PermsServiceImpl permsService;
/**
* 执行授权逻辑
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑");
//给资源授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//到数据库查询当前登录用户的授权字符串
//获取当前登录用户
Subject subject = SecurityUtils.getSubject();
//传对象因为热部署的原因没办法转换
Integer uid = (Integer) subject.getPrincipal();
QueryWrapper<Perms> wrapper = new QueryWrapper<>();
wrapper.eq("uid",uid);
List<Perms> permsList = permsService.list(wrapper);
for (Perms perms : permsList) {
info.addStringPermission(perms.getPerms());
}
return info;
}
/**
* 执行认证逻辑
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证逻辑");
//编写shiro判断逻辑
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username",token.getUsername());
User user = userService.getOne(wrapper);
if(user==null){
//用户名不存在
return null;//shiro底层会跑出UnknownAccountExpection
}
//密码校验
return new SimpleAuthenticationInfo(user.getId(),user.getPassword(),"");
}
}
代码解析
创建Realm对象,供DefaultWebSecurityManager依赖使用。
DefaultWebSecurityManager对象用哪个与对安全事务的管理,需要注入Realm对象实现对事务的处理。
ShiroFilterFactoryBean是shiro拦截器的bean工厂,这里面配置shiro的拦截请求的集合,以及放行集合。下面是shiro拦截器的内置过滤器种类。
/**
* shiro内置过滤器,可以实现权限相关的拦截器
* 常用的过滤器:
* anon:无需认证(登录),可以访问
* authc:必须认证才可以访问
* user:如果使用了Rememberme的功能可以访问
* perms:该资源必须得到资源权限才可以访问
* role:该资源必须得到角色权限才可以访问
*/
通常使用LinkHashMap<>存储设置的请求。
LinkedHashMap 是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比 LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。
为了保证数据的顺序行,所以采用了LinkHashMap
需要注意的是:
放行权限需要先设置,后面再设置授权和登录权限,最后设置拦截所有的页面
否则会由于执行顺序的问题导致部分配置会失效。
shiro执行流程路
执行分析:外部发来请求,shirofilter首先根据配置文件判断是否需要拦截,如果需要拦截并且是没有认证的则直接跳转到登录页面进行登录认证和授权,如果不需要拦截则直接放行。