SpringBoot整合Shiro
SpringBoot整合Shiro使用
哪里不懂点哪里官网:http://shiro.apache.org/
一、简介
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。
Java领域中spring security(原名Acegi)也是一个开源的权限管理框架,但是spring security依赖spring运行,而shiro就相对独立,最主要是因为shiro使用简单、灵活,所以现在越来越多的用户选择shiro
tips:1.4版本前后有区别的,需注意一下
关键词介绍
1 Subject
Subject即主体,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权
2 SecurityManager
SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。
SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。
3 Authenticator
Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。
4 Authorizer
Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。
5 realm
Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。
注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。
6 sessionManager
sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。
7 SessionDAO
SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。
8 CacheManager
CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。
9 Cryptography
Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。
二、使用(基于springboot)
1、pom依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>${shiro.version}</version>
</dependency>
2、简约yml配置,按需配置即可
shiro: web: enabled: true #表示开启 shiro unauthorizedUrl: /doLogin #访问未获授权的页面时默认的跳转路径如404,401 successUrl: /hello #登录成功的跳转页面 loginUrl: /login #登录页面
3、ShiroConfig注入
@Configuration public class ShiroConfig { //创建realm对象,需要自定义类:1 @Bean public MyRealm myRealm() { return new MyRealm(); } //DefaultWebSecurityManager:2 @Bean public DefaultWebSecurityManager securityManager(@Qualifier("myRealm") MyRealm myRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myRealm); return securityManager; } //ShiroFilterFactoryBean:3 @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); //设置安全管理器 bean.setSecurityManager(securityManager); //添加shiro内置过滤器 Map <String, String> filterMap = new LinkedHashMap <>(); //anon: 无需认证即可访问 //authc: 需要认证才可访问 //user: 点击“记住我”功能可访问 //perms: 拥有权限才可以访问 //role: 拥有某个角色权限才能访问 filterMap.put("/api/*", "authc"); bean.setFilterChainDefinitionMap(filterMap); bean.setLoginUrl("/doLogin"); return bean; } }
4、自定义Realm类
@Slf4j public class MyRealm extends AuthorizingRealm { @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { log.info("鉴权");//用户--(关系)--角色--(关系)--权限(数据库或缓存获取匹配) SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //Admin admin = (Admin) principalCollection.getPrimaryPrincipal(); //Role role = adminService.getRoleName(admin.getId()); Set <String> permission = new HashSet <>();//比数组快,不用一个个遍历索引去访问 if(true) { info.addRole("admin"); permission.add("111"); permission.add("333"); } else { info.addRole("user"); permission.add("222"); } info.addStringPermissions(permission); return info; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { log.info("认证"); String username = (String) token.getPrincipal(); Admin userLogin = new Admin();//数据库或缓存取用户信息匹配 userLogin.setUserName("admin"); userLogin.setPassword("111111"); userLogin.setIsLock(0); if(userLogin == null) { throw new UnknownAccountException("账户不存在!"); } if(userLogin.getIsLock() == 1) { log.info("当前登录的用户被锁定"); throw new LockedAccountException();//抛出异常 } SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userLogin, DigestUtils.md5DigestAsHex(userLogin.getPassword().getBytes()), ByteSource.Util.bytes(userLogin.getUserName()), getName()); userLogin.setPassword(""); //将用户信息放入session中 SecurityUtils.getSubject().getSession().setAttribute(GlobalConstant.SESSION_USER_INFO, userLogin); return authenticationInfo; } }
5、实际操作
a、接口添加注解,可选(问题是需要控制器统一捕捉异常,看实际需要)
@RequiresRoles(value = "admin", logical = Logical.OR) @RequiresPermissions(value ="222",logical=Logical.OR) @RequiresAuthentication @RequiresUser
b、使用拦截器,实现 HandlerInterceptor 接口重写 preHandle
@Slf4j public class IsLockInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 从参数request中获取session HttpSession session = request.getSession(); // 从session中获取用户id Object id = session.getAttribute("id"); if (id == null) { // 用户未登录过 response.sendRedirect("404.html"); // 执行拦截,阻止运行 return false; } /* if(id == null){ return true; }*/ AdminService adminService = (AdminService) SpringUtil.getBean("adminServiceImpl"); Admin admin = adminService.getById(Integer.valueOf(id.toString())); Integer isLock = admin.getIsLock(); // 判断锁定状态 if (isLock == 0) { // 用户锁定 log.info("用户被锁定,将被重定向到登录页面"); response.sendRedirect("/login"); // 执行拦截,阻止运行 return false; } // 未锁定,直接放行 return true; } }
c、基于角色访问
if(SecurityUtils.getSubject().hasRole("角色ID")){ //具有该角色ID可以操作的,缺陷系统可扩展性差 }
d、基于资源访问
if(SecurityUtils.getSubject().isPermitted("权限标识")){ //具有该权限可以操作,可扩展性强 }
e、控制器使用
Subject subject = SecurityUtils.getSubject(); Object object = subject.getPrincipal(); Session session = subject.getSession(); Object o = session.getAttribute(CookieConstant.SESSION_USER_INFO);//此处获取在认证存的用户session PrincipalCollection principalCollection = subject.getPreviousPrincipals(); PrincipalCollection principals = subject.getPrincipals();
三、流程图(暂时推荐官网)
浙公网安备 33010602011771号