Shiro介绍就跳过了,这里记录怎么在项目中使用
1 添加maven依赖
<shiro.version>1.4.0</shiro.version>
<druid.version>1.1.20</druid.version>
<shiro-redis.version>3.2.3</shiro-redis.version>
<dependency>
<!--session持久化插件-->
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>${shiro-redis.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- shiro-core -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
2 建好 用户表(我这里是whaccount),角色表(role),用户角色中间表,权限表(或者叫菜单表,menu),角色-权限(菜单)中间表这5张表
具体的RBAC概念这里我就不细说了 网上一大堆解释,用户对应角色,角色对应权限
3 实现自定义Realm
package com.hy.bjggwhy.config; import com.hy.bjggwhy.entity.WhyMenu; import com.hy.bjggwhy.entity.WhyRole; import com.hy.bjggwhy.entity.WhyWhaccount; import com.hy.bjggwhy.service.WhyMenuService; import com.hy.bjggwhy.service.WhyRoleService; import com.hy.bjggwhy.service.WhyWhaccountService; import com.hy.bjggwhy.util.Utils; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; /** * @author zhangmy * @date 2019-12-24 15:57 * @description 配置Realm类(shiro框架手动配置的关键,用来登陆和权限认证) */ public class WhyShiroRealm extends AuthorizingRealm { private static final Logger LOGGER = LoggerFactory.getLogger(WhyShiroRealm.class); @Autowired private WhyWhaccountService whaccountService; @Autowired private WhyRoleService roleService; @Autowired private WhyMenuService menuService; //授权配置 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { LOGGER.info("权限配置-->WhyShiroRealm.doGetAuthorizationInfo:"); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); WhyWhaccount whaccount = (WhyWhaccount)principals.getPrimaryPrincipal(); try { List<WhyRole> roles = roleService.getRoleByWhaccountId(whaccount.getId()); for (WhyRole role : roles) { authorizationInfo.addRole(role.getName());//角色存储 } //此处如果多个角色都拥有某项权限,不会数据重复,内部用的是Set List<WhyMenu> menus = menuService.getMenuByRoleList(roles); for (WhyMenu menu : menus) { authorizationInfo.addStringPermission(menu.getUrl());//权限存储 } } catch (Exception e) { Utils.getExceptionDetail(e); } return authorizationInfo; } //认证配置 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { LOGGER.info("Shiro认证-->WhyShiroRealm.doGetAuthenticationInfo:"); //获取用户的输入的账号. String username = (String) token.getPrincipal(); WhyWhaccount whaccount = whaccountService.findWhaccountByUsername(username); if (whaccount == null) { return null; } SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( whaccount, //用户名 whaccount.getPassword(), //密码 null,//salt getName() //realm name ); return authenticationInfo; } }
4 添加Shiro配置,通过ShiroConfig类实现
package com.hy.bjggwhy.config; 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.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.filter.DelegatingFilterProxy; import java.util.LinkedHashMap; import java.util.Map; /** * @author zhangmy * @date 2019-12-24 16:46 * @description */ @Configuration //@ComponentScan public class ShiroConfig { @Bean public FilterRegistrationBean delegatingFilterProxy() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); DelegatingFilterProxy proxy = new DelegatingFilterProxy(); proxy.setTargetFilterLifecycle(true); proxy.setTargetBeanName("shiroFilter"); filterRegistrationBean.setFilter(proxy); return filterRegistrationBean; } @Bean("shiroFilter") public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必须设置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // setLoginUrl 如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射 // shiroFilterFactoryBean.setLoginUrl("/login"); //设置成功跳转的页面 //shiroFilterFactoryBean.setSuccessUrl("/index"); // 设置无权限时跳转的 url; //shiroFilterFactoryBean.setUnauthorizedUrl("/notRole"); // 设置拦截器 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); //开放接口 // filterChainDefinitionMap.put("/**", "anon");//放开全部 filterChainDefinitionMap.put("/whaccount/whaccountLogin", "anon"); //放开登录 filterChainDefinitionMap.put("/*/get*", "anon"); //放开查询 filterChainDefinitionMap.put("/upload/*", "anon"); //放开资源上传 filterChainDefinitionMap.put("/search/*", "anon"); //放开资源搜索 filterChainDefinitionMap.put("/user/*", "anon"); //放开普通用户操作 filterChainDefinitionMap.put("/swagger-ui.html", "anon"); //放开swagger filterChainDefinitionMap.put("/swagger-resources/**", "anon"); //放开swagger filterChainDefinitionMap.put("/v2/**", "anon"); //放开swagger filterChainDefinitionMap.put("/webjars/**", "anon"); //放开swagger filterChainDefinitionMap.put("/**", "authc");//其余接口一律拦截,这行代码必须放在所有权限设置的最后,不然都被拦截 //配置shiro默认登录界面地址,前后端分离中登录界面跳转应由前端路由控制,后台仅返回json数据 //shiroFilterFactoryBean.setLoginUrl("/unauth"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); System.out.println("Shiro拦截器工厂类注入成功"); return shiroFilterFactoryBean; } /* 自定义身份认证realm 必须写上这个类,并加上@Bean注解,目的是注入CustomRealm 否则会影响CustomRealm类中其他类的依赖注入 */ @Bean public WhyShiroRealm customRealm(){ WhyShiroRealm shiroRealm = new WhyShiroRealm(); //shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());// 将md5密码比对器传给realm return shiroRealm; } /* 注入securityManager */ @Bean public SecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //设置REALM securityManager.setRealm(customRealm()); return securityManager; } /* 开启注解支持 */ @Bean //@DependsOn({"lifecycleBeanPostProcessor"}) public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){ DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){ AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager()); return authorizationAttributeSourceAdvisor; } }
5 登录接口做shiro认证
private Response login(String username, String password){ //shiro登录验证 Subject subject = SecurityUtils.getSubject(); String realPassword = MD5Utils.GetMD5Code(password); UsernamePasswordToken token = new UsernamePasswordToken(username, realPassword); subject.login(token); //用户基本信息 WhyWhaccount whyWhaccount = this.getWhaccountByUsername(username); Jedis jedis = RedisUtil.getJedis(); String key = MD5Utils.GetMD5Code(Constant.REDIS_WHACCOUNT_USER_KEY_PREFIX + whyWhaccount.getId()); String sessionValue = MD5Utils.GetMD5Code(whyWhaccount.getId() + whyWhaccount.getUserName() + whyWhaccount.getPassword()); jedis.set(key, sessionValue); jedis.expire(key, Constant.REDIS_SESSION_MAX_INACTIVE_INTERNAL); Map<String, Object> loginMap = new HashMap<>(); whyWhaccount.setPassword(null); loginMap.put("whaccount",whyWhaccount); loginMap.put("session",sessionValue); //拥有的角色 List<WhyRole> roles = roleService.getRoleByWhaccountId(whyWhaccount.getId()); loginMap.put("roles", roles); //拥有的菜单权限 List<WhyMenu> menus = menuService.getMenuByRoleList(roles); loginMap.put("menus", menus); return new Response(Constant.RES_SUCCESS, "登录成功", loginMap); }
6 Shiro授权
授权方式比较多,我这里使用注解方式,即在需要授权验证的controller上面使用
@RequiresPermissions({"权限的url"})
shiro会根据需要的权限url再去subject的权限中去对比,有就通过,没有就不通过