shiro开发
shiro源码解读
shiro默认拦截器
anon(AnonymousFilter.class),
authc(FormAuthenticationFilter.class),
authcBasic(BasicHttpAuthenticationFilter.class),
logout(LogoutFilter.class),
noSessionCreation(NoSessionCreationFilter.class),
perms(PermissionsAuthorizationFilter.class),
port(PortFilter.class),
rest(HttpMethodPermissionFilter.class),
roles(RolesAuthorizationFilter.class),
ssl(SslFilter.class),
user(UserFilter.class);
复制代码
createFilterChainManager
- 对每一个默认的过滤器调用applyGlobalPropertiesIfNecessary进行设置
- 对每一个自定义的过滤器进行设置并添加到过滤器管理器DefaultFilterChainManager中
- 第三是调用createChain构造chain(过滤器链:记录一个url和过滤器之间一对多的关系)
shiro知识总结
为什么选择shiro
- 从外部来看:具有非常简单易于使用的API
- 从内部来看:有一个可扩展的架构
shiro提供了哪些功能
- 认证
Authentication
:身份认证/登录 - 授权
Authorization
:即权限验证 - 加密
Cryptography
:保护数据的安全性 - 会话管理
Session Manager
:用户登是一次会话 - Web集成
Web Support
:非常容易的集成到Web环境 - 缓存
Caching
:用户登录后,其用户信息、拥有的角色/权限缓存 - 匿名
Run As
:允许一个用户假装为另一个用户 - 记住我
Remember Me
:一次登录后再次访问不用登录
shiro常见名词
- Subject:主体,可以是任何可以与应用交互的“用户”;
- SecurityManager:相当于SpringMVC中的DispatcherServlet,所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理;
- Authenticator:认证器,负责主体认证的
- Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
- Realm:可以有1个或多个Realm,可以认为是安全实体数据源;
- SessionManager:Shiro抽象了一个自己的Session来管理主体与应用之间交互的数据;
- CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;
- Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的。
shiro 原理简介
身份认证步骤
- 传入用户的身份(用户名<唯一标识>)和凭证(密码):UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
- 调用Subject.login(token)进行登录
- 自动委托给Security Manager
- 委托给Authenticator进行身份验证
- (传入凭证信息)委托给相应的AuthenticationStrategy进行多Realm(数据源)身份验证
身份认证实现
1.继承AuthorizingRealm(授权)即可;其继承了AuthenticatingRealm(即身份验证),而且也间接继承了CachingRealm(带有缓存实现)(注:也可以直接implements Realm)
身份鉴权实现
shiro开发实现
- 1 继承extends AuthorizingRealm提供安全数据源
/**
* @Title
* @Description
* @param principalCollection
* @return org.apache.shiro.authz.AuthorizationInfo
* @date 2017/8/8 下午1:36
* @author tush
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
//得到用户身份信息
String username = principalCollection.getPrimaryPrincipal().toString();
//得到用户权限信息
ShiroUserRolePermission shiroUserRolePermission = null;
try {
shiroUserRolePermission = provideUserRolePermissionInfo(username);
} catch (PendingException e) {
ShiroLogger.info("shiro get redis cache catch a exception ....",e);
}
//获取的用户信息为空 可能出现异常
if (shiroUserRolePermission != null){
Set<String> roleSet = shiroUserRolePermission.getRoleSet();
//得到用户的权限列表
Set<ShiroPermission> permissionList = shiroUserRolePermission.getPermissionSet();
Set<Permission> bitPermissions = new HashSet<>();
//得到权限实例
permissionList.forEach(permission -> {
ShiroPermission param = new ShiroPermission().builder()
.actionUrl(permission.getActionUrl())
.permissionCode(permission.getPermissionCode())
.instanceId(permission.getInstanceId())
.build();
bitPermissions.add(new DefinitionBitPermission(JackSonUtils.bean2Json(param)));
});
if (null != bitPermissions) {
ShiroLogger.info("成功获取{}用户权限信息",username);
authorizationInfo.addObjectPermissions(bitPermissions);
}
if (null != roleSet) {
ShiroLogger.info("成功获取{}用户角色信息",username);
authorizationInfo.addRoles(roleSet);
}
}
return authorizationInfo;
}
/**
* @Title
* @Description 登录认证
* @param authenticationToken 身份token
* @return org.apache.shiro.authc.AuthenticationInfo
* @date 2017/8/16 上午10:21
* @author tush
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//用户唯一标识
String username = authenticationToken.getPrincipal().toString();
//用户信息
// ShiroUser user = null;
// try {
// user = provideUserRolePermissionInfo(username).getUser();
// } catch (PendingException e) {
// ShiroLogger.error("shiro get redis cache catch a exception ....",e);
// }
// if (user == null){
// throw new UnknownAccountException();
// }
String password = new String((char[])authenticationToken.getCredentials());
// if (!password.equals(user.getPassword())) {
// throw new UnknownAccountException();
// }
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,password,getName());
return info;
}
复制代码
- 权限比较实体转换器
public class BitAndWildPermissionResolver implements PermissionResolver {
private static final String JSON_START = "{";
private static final String JSON_END = "}";
/**
* @Title
* @Description Json权限准换成权限元数据
* @param permissionStr
* @return org.apache.shiro.authz.Permission
* @date 2017/8/16 上午10:20
* @author tush
*/
@Override
public Permission resolvePermission(String permissionStr) {
//拦截器传入的json对象转换成permission元数据
if (permissionStr.startsWith(JSON_START)&&permissionStr.endsWith(JSON_END)) {
return new DefinitionBitPermission(permissionStr);
}
return new WildcardPermission(permissionStr);
}
}
复制代码
- 自定义权限比较实体对象
public class DefinitionBitPermission implements Permission,Serializable{
/**
* @Title
* @Description 权限比较方法
* @param permission
* @return boolean
* @date 2017/8/8 11:29
* @author tush
*/
@Override
public boolean implies(Permission permission) {
if(!(permission instanceof DefinitionBitPermission)) {
return false;
}
DefinitionBitPermission bitPermission = (DefinitionBitPermission)permission;
if (bitPermission.param.getPermissionCode()>15) {
return false;
}
if(!(DEFAULT_PERMISSION.equals(this.param.getActionUrl()) || this.param.getActionUrl().equals(bitPermission.param.getActionUrl()))) {
return false;
}
if(this.param.getPermissionCode() == 0 || (this.param.getPermissionCode() & bitPermission.param.getPermissionCode()) == 0) {
return false;
}
if(!(DEFAULT_PERMISSION.equals(this.param.getInstanceId()) || this.param.getInstanceId().equals(bitPermission.param.getInstanceId()))) {
return false;
}
return true;
}
}
复制代码
- 自定义拦截器
class DefinitionAccessControlFilter extends AccessControlFilter {
/**
* @Title
* @Description 鉴权
* @param servletRequest
* @param servletResponse
* @param o
* @return boolean
* @date 2017/8/8 上午11:09
* @author tush
*/
@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
//获取实体
ShiroLogger.info("shiro 权限检查开始....");
Subject subject = getSubject(servletRequest,servletResponse);
if (subject.getPrincipal()==null ){
return false;
}
//超级管理员权限处理
if (subject.getSession().getAttribute(ADM_FLAG) != null){
Object obj = subject.getSession().getAttribute(ADM_FLAG);
if (obj instanceof Integer) {
if (IS_ADM_STRING == obj) {
return true;
}
}else if (obj instanceof String){
if (IS_ADM_STRING.equals(obj)) {
return true;
}
}
}
//得到请求地址
String url = getPathWithinApplication(servletRequest);
//得到请求方法
String method = ((HttpServletRequest) servletRequest).getMethod();
//权限参数初始化
ShiroPermission param = new ShiroPermission();
//将方法标识转换成权限位
if (method.equals(SHIRO_GET)) {
param.setPermissionCode(BINARY_EIGHT);
} else if (method.equals(SHIRO_POST)){
param.setPermissionCode(BINARY_FOUR);
} else if (method.equals(SHIRO_PUT)) {
param.setPermissionCode(BINARY_TWO);
} else {
param.setPermissionCode(BINARY_ONE);
}
//设置请求地址
param.setActionUrl(url);
//设置实例名称:默认全部实例
//param.setInstanceId("*");
//开始鉴权
return subject.isPermitted(new DefinitionBitPermission(JackSonUtils.bean2Json(param)));
}
}
复制代码
- 自定义权限注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})//适用的地方:方法上
public @interface CheckPermission {
/** 权限地址 **/
@NotNull
String actionUrl();
/** 权限位 **/
PermissionMethod permissionCode() default PermissionMethod.GET;//默认没有任何权限
/** 权限实例 **/
String instanceId() default "*";//默认全部实例
}
复制代码
- 权限注解过滤器实现
@Aspect
@Component
public class ExtPermissionInterceptor {
private static final String DEFAULT_PERMISSION = "*";
/**
* @Title
* @Description 权限检查实体注解
* @param pjp
* @param checkPermission
* @return java.lang.Object
* @date 2017/8/16 上午9:54
* @author tush
*/
@Around("@annotation(checkPermission)")
public Object doInterceptor(ProceedingJoinPoint pjp, CheckPermission checkPermission) throws Throwable {
boolean isPermission = false;
Subject subject = SecurityUtils.getSubject();
if (subject==null||subject.getPrincipal()==null){
ShiroLogger.info("ExtPermissionInterceptor say:you have no login");
throw new UnknownAccountException("session 失效");
}
//没有获得注解 及不需要权限-- 则直接运行
if (null != checkPermission) {
String actionUrl = checkPermission.actionUrl();
PermissionMethod method = checkPermission.permissionCode();
int permissionCode = method.getValue();
String instanceId = checkPermission.instanceId();
ShiroPermission permission = new ShiroPermission();
permission.setActionUrl(actionUrl);
permission.setPermissionCode(permissionCode);
permission.setInstanceId(StringUtil.isNotEmpty(instanceId)?instanceId:DEFAULT_PERMISSION);
//当前登录人 具有权限
if (subject.isPermitted(new DefinitionBitPermission(JackSonUtils.bean2Json(permission)))) {
isPermission = true;
}
} else {
isPermission = true;
}
if (isPermission) {
//有执行方法或权限不拦截
ShiroLogger.info("annotation checkPermission grant permission");
return pjp.proceed();
} else {
ShiroLogger.info("you have no permission....");
throw new UnauthorizedException("没有访问权限");
}
}
}
复制代码
- cache 实现
public class CustomShiroCacheManager implements CacheManager, Destroyable {
private ShiroCacheManager shiroCacheManager;
@Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
return getShiroCacheManager().getCache(name);
}
@Override
public void destroy() throws Exception {
shiroCacheManager.destroy();
}
public ShiroCacheManager getShiroCacheManager() {
return shiroCacheManager;
}
public void setShiroCacheManager(ShiroCacheManager shiroCacheManager) {
this.shiroCacheManager = shiroCacheManager;
}
}
复制代码
- shiro 配置文件(交由spring管理)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
">
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 开启Shiro注解 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!-- 用户缓存 -->
<bean id="cacheManager" class="com.weiyi.pay.framework.shiro.cache.jedis.CustomShiroCacheManager">
<property name="shiroCacheManager" ref="definitionShiroCacheManager"/>
</bean>
<!-- shiro 缓存实现,对ShiroCacheManager,采用redis的实现 -->
<bean id="definitionShiroCacheManager" class="com.weiyi.pay.framework.shiro.cache.impl.DefinitionShiroCacheManager">
<property name="jedisManager" ref="jedisManager"/>
</bean>
<!-- redis 的缓存 -->
<bean id="jedisManager" class="com.weiyi.pay.framework.shiro.cache.jedis.JedisManager"/>
<!-- 自定义Realm -->
<bean id="weDefinitionAuthorizingRealm" class="com.weiyi.pay.framework.shiro.realm.WeDefinitionAuthorizingRealm">
<property name="authorizationCachingEnabled" value="true"/>
</bean>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="weDefinitionAuthorizingRealm"/>
<!-- 注入缓存管理器 -->
<property name="cacheManager" ref="cacheManager"/>
</bean>
<!--自定义登录状态检查 -->
<bean id="loginAuthc" class="com.weiyi.pay.framework.shiro.filter.DefinitionAuthenticationFilter"/>
<!--自定义二进制鉴权 -->
<bean id="biteAuthc" class="com.weiyi.pay.framework.shiro.filter.DefinitionAccessControlFilter"/>
<bean id="anonAuthc" class="com.weiyi.pay.framework.shiro.filter.DefinitionAnonymousFilter"/>
</beans>
复制代码
-
session实现
集成springs-session