核心过滤器
<filter>
<filter-name>shiroFilterfilter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
filter>
<filter-mapping>
<filter-name>shiroFilterfilter-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...!!!";
}
}
@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;
}
}
public enum DefaultFilter {
// ....
perms(PermissionsAuthorizationFilter.class),
roles(RolesAuthorizationFilter.class),
// ....
}
filterChainDefinitionMap.put("/item/select","roles[超级管理员,运营]");
filterChainDefinitionMap.put("/item/delete","perms[item:delete,item:insert]");
/**
* 在要求的多个角色中,有一个满足要求,就放行
* @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;
}
}
@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>