1、Shiro重要概念有哪些?
Subject:可以理解为与shiro打交道的对象,该对象封装了一些对方的信息,shiro可以通过subject拿到这些信息。
SecurityManager:即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过指使Authorizer和Authenticator等对subject进行授权和身份验证等工作。
Realm:管理着一些如用户、角色、权限等重要信息,Shiro中所需的这些重要信息都是从Realm这里获取的,Realm本质上就是一个重要信息的数据源。
Authenticator:认证器,负责Subject的认证操作,认证过程就是根据Subject提供的信息通过Realm查询到相关信息,然后做对比,支持扩展。
Authorizer:授权器,控制着Subject对服务资源的访问权限。
SessionManager:用于管理Session,这个Session可以是web的也可以不是web的。
SessionDao:把Session的 CRUD和存储介质联系起来的工具,存储介质可以是数据库,也可以是缓存,比如把session放到redis里面。
CacheManager:缓存控制器,Realm管理的数据(用户、角色、权限)可以放到缓存里由CacheManager管理,提高认证授权等的速度。
Cryptography:加密组件,Shiro提供了很多加解密算法的组件。
1.1、Shiro的主要流程
Authentication和Authenticator的主要流程
2、相关知识
2.1、Shiro为SessionManager提供了三个默认实现
DefaultSessionManager:DefaultSecurityManager使用的默认实现,用于JavaSE环境;
ServletContainerSessionManager:DefaultWebSecurityManager使用的默认实现,用于Web环境,其直接使用Servlet容器的会话;
DefaultWebSessionManager:用于Web环境的实现,可以替代ServletContainerSessionManager,自己维护着会话,直接废弃了Servlet容器的会话管理。
3、使用示例
maven:
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
实现 AuthenticationToken 接口写个 StatelessToken 类:
import com.xxx.common.utils.JwtUtil;
import org.apache.shiro.authc.AuthenticationToken;
public class StatelessToken implements AuthenticationToken {
private String username;
private String token;
public StatelessToken(String token) {
this.username = JwtUtil.getUserNameFromToken(token);
this.token = token;
}
@Override
public Object getPrincipal() {
return username;
}
@Override
public Object getCredentials() {
return token;
}
}
继承 FormAuthenticationFilter 类写个 ShiroFormAuthenticationFilter 类:
import com.xxx.common.result.Result;
import com.xxx.common.result.ResultEnum;
import com.xxx.common.result.ResultUtil;
import com.xxx.common.utils.JwtUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
public class ShiroFormAuthenticationFilter extends FormAuthenticationFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final String AUTHORIZATION = "Authorization";
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
return false;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest req = (HttpServletRequest) request;
String token = req.getHeader(AUTHORIZATION);
if (JwtUtil.validateToken(token)) {
StatelessToken statelessToken = new StatelessToken(token);
try {
//委托realm进行登录认证
getSubject(request, response).login(statelessToken);
return true;
} catch (Exception e) {
return false;
}
}
HttpServletResponse resp = (HttpServletResponse) response;
if (req.getMethod().equals(RequestMethod.OPTIONS.name())) {
resp.setStatus(HttpStatus.OK.value());
return true;
} else {
Result result = ResultUtil.error(ResultEnum.USER_UN_LOGIN.getCode(), ResultEnum.USER_UN_LOGIN.getMsg());
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(result);
resp.setContentType("application/json");
resp.setCharacterEncoding("UTF-8");
PrintWriter out = resp.getWriter();
out.println(json);
out.flush();
out.close();
logger.info(ResultEnum.USER_UN_LOGIN.getMsg());
return false;
}
}
}
继承 AuthorizingRealm 类写一个 MyRealm 类:
import java.util.List;
import com.xxx.admin.mapper.PermissionModelMapper;
import com.xxx.admin.mapper.UserModelMapper;
import com.xxx.admin.po.PermissionModel;
import com.xxx.admin.po.UserModel;
import com.xxx.admin.vo.resp.MenuRespVO;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserModelMapper userModelMapper;
@Autowired
private PermissionModelMapper permissionModelMapper;
@Override
public boolean supports(AuthenticationToken token) {
//仅支持StatelessToken类型的Token
return token instanceof StatelessToken;
}
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
UserModel user=(UserModel) principals.getPrimaryPrincipal();
Integer userId=user.getId();
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
List<MenuRespVO> permissionsByUserName = permissionModelMapper.getPermissionsByUserId(userId);
for(PermissionModel permission:permissionsByUserName) {
info.addStringPermission(permission.getPermissionname());
}
return info;
}
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String userName = token.getPrincipal().toString();
UserModel user = userModelMapper.selectByUserName(userName);
if (user != null) {
StatelessToken statelessToken = (StatelessToken) token;
AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user, statelessToken.getCredentials(), getName());
return authcInfo;
} else {
return null;
}
}
}
ShiroConfig 类:
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.shiro.mgt.SecurityManager;
import javax.servlet.Filter;
@Configuration
public class ShiroConfig {
/**
* ShiroFilterFactoryBean 处理拦截资源文件问题。
* 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在
* 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
*
* Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过
* 3、部分过滤器可指定参数,如perms,roles
*
*/
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 这里使用自定义的filter
LinkedHashMap<String, Filter> filtsMap = new LinkedHashMap<>();
filtsMap.put("authc", new ShiroFormAuthenticationFilter());
shiroFilterFactoryBean.setFilters(filtsMap);
//如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
//拦截器
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
//配置不会被拦截的链接,顺序判断
filterChainDefinitionMap.put("/user/login", "anon");
filterChainDefinitionMap.put("/user/forgetPassword", "anon");
filterChainDefinitionMap.put("/user/sendCode", "anon");
//swagger用
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
filterChainDefinitionMap.put("/swagger-resources/**", "anon");
filterChainDefinitionMap.put("/v2/api-docs", "anon");
filterChainDefinitionMap.put("/webjars/springfox-swagger-ui/**", "anon");
//过滤链定义,从上向下顺序执行,一般将 /**放在最为下边
//① authc:所有url都必须认证通过才可以访问; ② anon:所有url都都可以匿名访问
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public SessionManager sessionManager(){
// //将我们继承后重写的shiro session 注册
// DemoSessionManager demoSessionManager = new DemoSessionManager();
// //如果后续考虑多tomcat部署应用,可以使用shiro-redis开源插件来做session 的控制,或者nginx 的负载均衡
// demoSessionManager.setSessionDAO(new DemoSessionManager2());
// return demoSessionManager;
// DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
// sessionManager.setSessionValidationSchedulerEnabled(true);
// //sessionManager.setSessionIdUrlRewritingEnabled(false);
// sessionManager.setSessionIdCookieEnabled(true);
// return sessionManager;
ServletContainerSessionManager servletContainerSessionManager = new ServletContainerSessionManager();
return servletContainerSessionManager;
}
@Bean
public SecurityManager securityManager(MyRealm myRealm, SessionManager sessionManager) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
// 设置realm.
defaultWebSecurityManager.setRealm(myRealm);
//自定义的shiro session 缓存管理器
defaultWebSecurityManager.setSessionManager(sessionManager);
return defaultWebSecurityManager;
}
/**
* Shiro生命周期处理器
* @return
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
* 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}