文章目录
- Shiro的Web流程
- Shiro整合SSM
- Shiro整合SpringBoot
- Shiro授权方式
- 过滤器链
- 自定义过滤器
- 注解
- 记住我
Shiro的Web流程
Shiro整合SSM
- 准备SSM的配置
- 准备经典五张表(见Shiro基本使用),完成测试
- 准备Shiro的配置
-
核心过滤器
<!-- 配置Shiro整合web的过滤器--> <filter> <!-- 默认情况下,请求到达这个过滤器,会去Spring容器中名字为filter-name的实例去处理--> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
-
准备shiroFilter实例
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> ..... </bean>
-
注入SecurityManager,登录页面路径,过滤器链
<!-- 构建realm--> <bean id="realm" class="com.xxx.realm.ShiroRealm" /> <!-- 构建securityManager--> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="realm"/> </bean> <!-- 构建ShiroFilter实例--> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/login.html" /> <property name="filterChainDefinitionMap"> <map> <entry key="/login.html" value="anon" /> <entry key="/user/**" value="anon" /> <entry key="/**" value="authc" /> </map> </property> </bean>
-
将ShiroRealm的模拟数据库操作,修改为与数据库交互(见Shiro基本使用)
-
编写登录功能,并测试效果
@PostMapping("/login") public String login(String username,String password){ // 执行Shiro的认证操作 //1. 直接基于SecurityUtils获取subject主体,不需要手动的将SecurityManager和SecurityUtils手动整合,Spring已经奥丁 Subject subject = SecurityUtils.getSubject(); //2. 发起认证 try { subject.login(new UsernamePasswordToken(username,password)); return "SUCCESS"; } catch (UnknownAccountException exception){ return "username fail!!!"; } catch (IncorrectCredentialsException exception){ return "password fail!!!"; } catch (AuthenticationException e) { return "donot know...!!!"; } }
-
Shiro整合SpringBoot
- 搭建SpringBoot工程
- 配置Shiro整合SpringBoot内容
@Configuration public class ShiroConfig { @Bean public DefaultWebSecurityManager securityManager(ShiroRealm realm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm); return securityManager; } @Bean public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){ DefaultShiroFilterChainDefinition shiroFilterChainDefinition = new DefaultShiroFilterChainDefinition(); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); filterChainDefinitionMap.put("/login.html","anon"); filterChainDefinitionMap.put("/user/**","anon"); filterChainDefinitionMap.put("/**","authc"); shiroFilterChainDefinition.addPathDefinitions(filterChainDefinitionMap); return shiroFilterChainDefinition; } }
Shiro授权方式
过滤器链
public enum DefaultFilter {
// ....
perms(PermissionsAuthorizationFilter.class),
roles(RolesAuthorizationFilter.class),
// ....
}
filterChainDefinitionMap.put("/item/select","roles[超级管理员,运营]");
filterChainDefinitionMap.put("/item/delete","perms[item:delete,item:insert]");
自定义过滤器
- 仿照RolesAuthorizationFilter实现自定义过滤器
/** * 在要求的多个角色中,有一个满足要求,就放行 * @author zjw * @description */ public class RolesOrAuthorizationFilter extends AuthorizationFilter { @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { // 获取主体subject Subject subject = getSubject(request, response); // 将传入的角色转成数组操作 String[] rolesArray = (String[]) mappedValue; // 健壮性校验 if (rolesArray == null || rolesArray.length == 0) { return true; } // 开始校验 for (String role : rolesArray) { if(subject.hasRole(role)){ return true; } } return false; } }
- 将自定义过滤器配置给Shiro
@Configuration public class ShiroConfig { @Bean public DefaultWebSecurityManager securityManager(ShiroRealm realm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm); return securityManager; } @Bean public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){ DefaultShiroFilterChainDefinition shiroFilterChainDefinition = new DefaultShiroFilterChainDefinition(); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); filterChainDefinitionMap.put("/login.html","anon"); filterChainDefinitionMap.put("/user/**","anon"); filterChainDefinitionMap.put("/item/select","rolesOr[超级管理员,运营]"); filterChainDefinitionMap.put("/item/delete","perms[item:delete,item:insert]"); filterChainDefinitionMap.put("/**","authc"); shiroFilterChainDefinition.addPathDefinitions(filterChainDefinitionMap); return shiroFilterChainDefinition; } @Value("#{ @environment['shiro.loginUrl'] ?: '/login.jsp' }") protected String loginUrl; @Value("#{ @environment['shiro.successUrl'] ?: '/' }") protected String successUrl; @Value("#{ @environment['shiro.unauthorizedUrl'] ?: null }") protected String unauthorizedUrl; @Bean protected ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager,ShiroFilterChainDefinition shiroFilterChainDefinition) { //1. 构建ShiroFilterFactoryBean工厂 ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean(); //2. 设置了大量的路径 filterFactoryBean.setLoginUrl(loginUrl); filterFactoryBean.setSuccessUrl(successUrl); filterFactoryBean.setUnauthorizedUrl(unauthorizedUrl); //3. 设置安全管理器 filterFactoryBean.setSecurityManager(securityManager); //4. 设置过滤器链 filterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition.getFilterChainMap()); //5. 设置自定义过滤器 , 这里一定要手动的new出来这个自定义过滤器,如果使用Spring管理自定义过滤器,会造成无法获取到Subject filterFactoryBean.getFilters().put("rolesOr",new RolesOrAuthorizationFilter()); //6. 返回工厂 return filterFactoryBean; } }
注解
-
注解进行授权时,是基于对Controller类进行代理,在前置增强中对请求进行权限校验
-
因为咱们使用SpringBoot的测试方式,直接在Controller方法上添加注解即可
@GetMapping("/update") @RequiresRoles(value = {"超级管理员","运营"}) public String update(){ return "item Update!!!"; } @GetMapping("/insert") @RequiresRoles(value = {"超级管理员","运营"},logical = Logical.OR) public String insert(){ return "item Update!!!"; } // @RequiresPermissions(value = "",logical = Logical.AND)
-
在SpringBoot中注解默认就生效,是因为自动装配中,已经配置好了对注解的支持
@Configuration @ConditionalOnProperty(name = "shiro.annotations.enabled", matchIfMissing = true) public class ShiroAnnotationProcessorAutoConfiguration extends AbstractShiroAnnotationProcessorConfiguration { @Bean @DependsOn("lifecycleBeanPostProcessor") @ConditionalOnMissingBean @Override public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { return super.defaultAdvisorAutoProxyCreator(); } @Bean @ConditionalOnMissingBean @Override public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { return super.authorizationAttributeSourceAdvisor(securityManager); } }
-
注解的形式无法将错误页面的信息定位到401.html,因为配置的这种路径,只针对过滤器链有效,注解无效。为了实现友好提示的效果,可以配置异常处理器,@RestControllerAdvice,@ControllerAdvice
记住我
记住我在开启后,可以针对一些安全级别相对更低的页面采用user过滤器拦截,只要登录过,不需要重新登录就可以访问。
准备两个接口
@GetMapping("/rememberMe")
public String rememberMe(){
return "rememberMe!!!";
}
@GetMapping("/authentication")
public String authentication(){
return "authentication!!!";
}
配置不同的过滤器
filterChainDefinitionMap.put("/item/rememberMe","user");
filterChainDefinitionMap.put("/item/authentication","authc");
在页面追加记住我按钮,并且在登录是,添加rememberMe效果
<form action="/user/login" method="post">
用户名:<input name="username" /> <br />
密码:<input name="password" /> <br />
记住我:<input type="checkbox" name="rememberMe" value="on" /> <br />
<button type="submit">登录</button>
</form>