shiro入门
参考: https://www.jianshu.com/p/7f724bec3dc3
1.引入maven依赖
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.apache.shiro shiro-spring 1.5.3 org.apache.shiro shiro-ehcache 1.5.3 org.projectlombok lombok true cn.hutool hutool-all 5.7.15
2.编写自定义Realm类继承AuthorizingRealm
package com.yxg.erp.config.shiro; import com.yxg.erp.common.shiro.Permissions; import com.yxg.erp.module.bean.Role; import com.yxg.erp.module.bean.User; import com.yxg.erp.module.service.LoginService; 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.util.StringUtils; public class CustomRealm extends AuthorizingRealm { @Autowired private LoginService loginService; /** * @MethodName doGetAuthorizationInfo * @Description 权限配置类 * @Param [principalCollection] * @Return AuthorizationInfo * @Author WangShiLin */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //获取登录用户名 String name = (String) principalCollection.getPrimaryPrincipal(); //查询用户名称 User user = loginService.getUserByName(name); //添加角色和权限 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); for (Role role : user.getRoles()) { //添加角色 simpleAuthorizationInfo.addRole(role.getRoleName()); //添加权限 for (Permissions permissions : role.getPermissions()) { simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsName()); } } return simpleAuthorizationInfo; } /** * @MethodName doGetAuthenticationInfo * @Description 认证配置类 * @Param [authenticationToken] * @Return AuthenticationInfo * @Author WangShiLin */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { if (StringUtils.isEmpty(authenticationToken.getPrincipal())) { return null; } //获取用户信息 String name = authenticationToken.getPrincipal().toString(); User user = loginService.getUserByName(name); if (user == null) { //这里返回后会报出对应异常 return null; } else { //这里验证authenticationToken和simpleAuthenticationInfo的信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), getName()); return simpleAuthenticationInfo; } } }
3.注入Spring相关配置
package com.yxg.erp.config.shiro; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.ObjectUtil; import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.config.ConfigurationException; import org.apache.shiro.io.ResourceUtils; import org.apache.shiro.mgt.SecurityManager; 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.tomcat.util.http.fileupload.IOUtils; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; @Configuration public class ShiroConfig { @Bean @ConditionalOnMissingBean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator(); defaultAAP.setProxyTargetClass(true); return defaultAAP; } /** * 缓存管理器 使用Ehcache实现 */ @Bean public EhCacheManager getEhCacheManager() { net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("ruoyi"); EhCacheManager em = new EhCacheManager(); if (ObjectUtil.isEmpty(cacheManager)) { em.setCacheManager(new net.sf.ehcache.CacheManager(getCacheManagerConfigFileInputStream())); return em; } else { em.setCacheManager(cacheManager); return em; } } /** * 返回配置文件流 避免ehcache配置文件一直被占用,无法完全销毁项目重新部署 */ protected InputStream getCacheManagerConfigFileInputStream() { String configFile = "classpath:ehcache/ehcache-shiro.xml"; InputStream inputStream = null; try { inputStream = ResourceUtils.getInputStreamForPath(configFile); byte[] b = IoUtil.readBytes(inputStream,true); InputStream in = new ByteArrayInputStream(b); return in; } catch (IOException e) { throw new ConfigurationException( "Unable to obtain input stream for cacheManagerConfigFile [" + configFile + "]", e); } finally { IOUtils.closeQuietly(inputStream); } } //将自己的验证方式加入容器 @Bean public CustomRealm myShiroRealm(EhCacheManager cacheManager) { CustomRealm customRealm = new CustomRealm(); customRealm.setCacheManager(cacheManager); return customRealm; } //权限管理,配置主要是Realm的管理认证 @Bean public SecurityManager securityManager(CustomRealm customRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(customRealm); return securityManager; } //Filter工厂,设置对应的过滤条件和跳转条件 @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Mapmap = new HashMap<>(); //登出 map.put("/logout", "logout"); //对所有用户认证 map.put("/**", "authc"); //登录 shiroFilterFactoryBean.setLoginUrl("/login"); //首页 shiroFilterFactoryBean.setSuccessUrl("/index"); //错误页面,认证不通过跳转 shiroFilterFactoryBean.setUnauthorizedUrl("/error"); shiroFilterFactoryBean.setFilterChainDefinitionMap(map); return shiroFilterFactoryBean; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }
原作者gitee代码地址: https://gitee.com/wsl6/shiro-demo
原理分析
1.自定义CustomRealm类继承AuthorizingRealm类
将CustomRealm注入Spring容器中
2.注入SecurityManager时将自定义CustomRealm设置进去
3.用户在登录时
调用Subject.loin(token); //token对象保存了登录名和密码
经过ModularRealmAuthenticator类中doAuthenticate(AuthenticationToken authenticationToken)
最终会到CustomRealm#doGetAuthenticationInfo方法进行登录认证
/** * 登录认证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken upToken = (UsernamePasswordToken) token; String username = upToken.getUsername(); String password = ""; if (upToken.getPassword() != null) { password = new String(upToken.getPassword()); } SysUser user = null; try { user = loginService.login(username, password); } catch (Exception e) { log.info("对用户[" + username + "]进行登录验证..验证未通过{}", e.getMessage()); throw new AuthenticationException(e.getMessage(), e); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName()); return info; }
在此方法中处理登录名和密码是否正确
4.登录成功用户在访问具体资源时
假如接口设置了@RequiresPermissions("system:user:view")
会先CustomRealm#doGetAuthorizationInfo方法进行权限认证
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { SysUser user = ShiroUtils.getSysUser(); // 角色列表 Setroles = new HashSet (); // 功能列表 Set menus = new HashSet (); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 管理员拥有所有权限 if (user.isAdmin()) { info.addRole("admin"); info.addStringPermission("*:*:*"); } else { roles = roleService.selectRoleKeys(user.getUserId()); menus = menuService.selectPermsByUserId(user.getUserId()); // 角色加入AuthorizationInfo认证对象 info.setRoles(roles); // 权限加入AuthorizationInfo认证对象 info.setStringPermissions(menus); } return info; }
注意: 在权限认证时候如果注入CustomRealm没有设置缓存会重复调用
建议设置缓存
//将自己的验证方式加入容器 @Bean public CustomRealm myShiroRealm(EhCacheManager cacheManager) { CustomRealm customRealm = new CustomRealm(); //设置缓存 customRealm.setCacheManager(cacheManager); return customRealm; }
默认使用ehcache缓存
redis使用方式参考: https://blog.csdn.net/qq_34021712/article/details/80791219
自定义RedisCacheManager 实现 CacheManager
注入到SecurityManager中
//配置redis缓存
securityManager.setCacheManager(cacheManager());
//配置自定义session管理,使用redis
securityManager.setSessionManager(sessionManager());