(十二)springboot中shiro的使用
一、引入maven配置
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency>
二、建表
用户表、角色表、权限表、用户角色表、角色权限表。
用户表:
角色表:
权限表:news:* 表示有新闻的所有权限(包括增删改查),而news:add,只有新闻的新增权限。
用户角色表:用户拥有哪些角色。
角色权限表:角色拥有哪些权限。
三、自定义Realm
自定义realm主要用于用户的权限验证以及登录验证。
自定义realm继承AuthorizingRealm类,并重写doGetAuthorizationInfo和doGetAuthenticationInfo方法,doGetAuthorizationInfo方法主要校验用户的权限,即该用户拥有什么角色以及权限。doGetAuthenticationInfo用于校验登录验证,即用户的用户名或者密码是否正确。
/** * 类名 : shiro的Realm * 用法 : * 创建人 : shyroke * 时间:2018/12/12 17:29 */ public class MyRealm extends AuthorizingRealm { @Autowired private UserMapper userMapper; @Autowired private PermissionMapper permissionMapper; /** * 授权,即该用户拥有什么角色以及权限 * 步骤:根据用户名获取角色以及权限,然后设置到验证信息类并返回。 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); String userName = principalCollection.getPrimaryPrincipal().toString(); // 获取该用户的角色 List<Role> roles = userMapper.getRolesByUserName(userName); Set<String> roleSets = new HashSet<>(); // 获取该用户的权限集 List<Permission> permissions = new ArrayList<>(); Set<String> permissoinSet = new HashSet<>(); //将List转为Set if(roles !=null && roles.size()>0){ for(Role role:roles){ roleSets.add(role.getName()); permissions.addAll(permissionMapper.getPermissionByRoleId(role.getId())); } } //将List转为Set if(permissions!=null & permissoinSet.size()>0){ for(Permission p :permissions){ permissoinSet.add(p.getName()); } } info.addRoles(roleSets); info.addStringPermissions(permissoinSet); return info; } /** * 认证,即用户账号密码是否正确 * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 获取用户名和密码 String userName = (String)token.getPrincipal(); String passWord = new String((char[]) token.getCredentials()); // 根据用户名查找该用户 User user = userMapper.getUserByName(userName); if(user == null){ throw new UnknownAccountException("用户名不存在"); } if(!user.getPassword().equals(passWord)){ throw new IncorrectCredentialsException("密码错误"); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getName(),user.getPassword(),getName()); return info; } }
四、shiro配置类
/** * 类名 :shiro的核心配置类 * 用法 : * 创建人 : shyroke * 时间:2018/12/14 15:20 */ @Configuration public class ShiroConfigration { // 设置自定义Realm @Bean public MyRealm myRealm(){ return new MyRealm(); } // 权限管理,配置主要是Realm的管理认证 @Bean public SecurityManager securityManager(){ DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRealm(myRealm()); return manager; } /** * 设置过滤条件和跳转条件 * anon 不生效的原因:1、map的类型必须是LinkedHashMap 2、anon必须定义在authc之前 * * @param securityManager * @return */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){ ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(securityManager); // 设置登录跳转 factoryBean.setLoginUrl("/admin"); factoryBean.setSuccessUrl("/admin/index"); //必须为LinkedHashMap 否则anon不生效 Map<String,String> map = new LinkedHashMap<>(); //退出 map.put("/admin/logout","logout"); //登录页面和登录验证不要拦截 map.put("/admin/login.html","anon"); map.put("/admin/tologin","anon"); //设置需要过滤的链接 map.put("/admin/**","authc"); factoryBean.setFilterChainDefinitionMap(map); return factoryBean; } /** * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 * 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可实现此功能 * @return */ @Bean public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){ DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator; } /** * 开启aop注解支持 * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }
五、调用
代码如下,当调用Subject.login方法后,会调用自定义Realm的doGetAuthenticationInfo方法校验用户名密码是否正确,如果不正确则抛出对应异常,controller层捕获并处理异常。
AuthenticationToken usernamePasswordToken = new UsernamePasswordToken(user.getName(),user.getPassword()); Subject subject = SecurityUtils.getSubject(); try { subject.login(usernamePasswordToken); }catch (UnknownAccountException ex) { logger.info("用户名错误!"); return R.error("用户名错误!"); } catch (IncorrectCredentialsException ex) { logger.info("密码错误!"); return R.error("密码错误!"); } catch (AuthenticationException ex) { logger.info(ex.getMessage()); return R.error("系统错误,请查看日志"); } catch (Exception ex) { logger.info(ex.getMessage()); return R.error("系统错误,请查看日志"); }