shiro的demo很多,我也看了很多,基本掌握了用法,而去基于shiro做一部分按需定制的开发还是感觉有些难度.
这个例子是这样的:
1.会员登录前端网站,需按照其等级分配权限,查看网站上的数据(低等级不展示具体数值,反之)
2.管理员等角色登录网站后端,进行后端权限操作分配.(除了样式没设置,其它的都还可以了)
我认为的改变是从ShiroConfiguration这个配置类着手,code 如下
/**
* ShiroFilterFactoryBean 处理拦截资源文件问题。
* 注意:单独一个ShiroFilterFactoryBean配置是或报错的,因为在
* 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
* <p>
* Filter Chain定义说明
* 1、一个URL可以配置多个Filter,使用逗号分隔
* 2、当设置多个过滤器时,全部验证通过,才视为通过
* 3、部分过滤器可指定参数,如perms,roles
*/
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//TODO 2019-1-29 17:40
//将设置url的参数改为application.properties中去配置
//securityManager改为可选择的进行传入
//customisedFilter的hashmap传入的url及URLPath...Filter改为参数可传入
//customisedFilter这个HashMap装配了登录及登录后可未授权页面(admin的配置admin的,member的配置member的)
//filterChainDefinitionMap
//1.securityManager中的realm按需配置
//2.shiroFilterFactoryBean中的set...Url为按需配置
//3.shiroFilterFactoryBean.setFilters(customisedFilter);中的customisedFilter按需配置
//**customisedFilter为put了仅仅一个"url",value为URL路径匹配过滤器(按需传入)
//4.shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
//**filterChainDefinitionMap为按需配置
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/admin/login"); //原为/login
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/admin/"); //原为/index
//未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/admin/unauthorized"); //原为/unauthorized
//拦截器.
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
//自定义拦截器
Map<String, Filter> customisedFilter = new HashMap<>();
customisedFilter.put("url", getURLPathMatchingFilter());
//配置映射关系 anon表示不需要权限即可访问
filterChainDefinitionMap.put("/admin/login", "anon");
// filterChainDefinitionMap.put("/admin/index", "anon");
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/admin/config/**", "anon");
filterChainDefinitionMap.put("/admin/doLogout", "logout");
;//后台登出
// 单独放入member的配置中 filterChainDefinitionMap.put("/member/doLogout","logout");//前端客户登出
filterChainDefinitionMap.put("/admin/**", "url"); //路径/shiro_admin/**全部需要进行权限验证
//其他资源都需要认证 authc 表示需要认证才能进行访问 user表示配置记住我或认证通过可以访问的地址
filterChainDefinitionMap.put("/admin/**", "user");
shiroFilterFactoryBean.setFilters(customisedFilter);
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
要分别设定其权限及验证有github上的一些demo可以参考,但不同的是没有了这些路径拦截配置.
ShiroFilterFactoryBean中有些数值的getter setter从这里入手,并重写FilterChainManager应该可以完成路径的自由配置:
同理可得重写ShiroFilterFactoryBean也一样会设定自由路径的配置:
首先第一步,在重写的CustomShiroFilterFactoryBean中,我们要设置参数的可判定性,因为上方中的loginUrl...包括put到filterChainDefinitionMap中的路径等都是固定的,我们要根据判断的用户类型去配给不同的路径.(当然这样做后在实际应用场景中是否能奏效还待考量)
根据这种思路改变的MyRealm
package com.tansuo365.test1.realm;
import com.tansuo365.test1.bean.Member;
import com.tansuo365.test1.bean.User;
import com.tansuo365.test1.entity.MyLoginInstance;
import com.tansuo365.test1.service.*;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
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.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Set;
@Component
public class MyRealm extends AuthorizingRealm {
@Autowired
private MemberService memberService;//member
@Autowired
private UserService userService;//user
@Autowired
private MroleService mroleService;//member
@Autowired
private RoleService roleService; //user
@Autowired
private MemberPermissionService memberPermissionService;//member
@Autowired
private PermissionService permissionService;//user
/*获取授权权限信息*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo info = null;
//获取当前登录类型
String loginType = (String)SecurityUtils.getSubject().getSession().getAttribute("loginType");
Set<String> permissions = null;
Set<String> roles = null;
// 能进入到这里,表示账号已经通过验证了
String username = (String)principals.getPrimaryPrincipal();
if(LoginEnum.ADMIN.toString().equals(loginType)){
permissions = permissionService.listPermissions(username);
roles = roleService.listRoleNames(username);
}
if(LoginEnum.MEMBER.toString().equals(loginType)){
// Member member = memberService.getByName(username);
// 通过service获取角色和权限
permissions = memberPermissionService.listMemberPermissions(username);
roles = mroleService.listMroleNames(username);
}
// 授权对象
info = new SimpleAuthorizationInfo();
// 把通过service获取到的角色和权限放进去
info.setStringPermissions(permissions);
info.setRoles(roles);
return info;
}
/*获取身份验证信息*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
User user = null;//后台用户
Member member = null;//前端会员
MyLoginInstance instance = null;
//1.把AuthenticationToken转换为CustomizedToken
CustomizedToken customizedToken = (CustomizedToken)token;
//2.从CustomizedToken中获取memberName
String nameInInput = customizedToken.getUsername();
//3.若用户不存在,抛出UnknownAccountException异常
String loginType = customizedToken.getLoginType();
if(LoginEnum.ADMIN.toString().equals(loginType)){
user = userService.getByName(nameInInput);
if(null == user){
throw new UnknownAccountException("管理员不存在!");
}
instance = user;
}
if(LoginEnum.MEMBER.toString().equals(loginType)){
member = memberService.getByName(nameInInput);
if(null == member){
throw new UnknownAccountException("会员不存在!");
}
instance = member;
}
//4.根据用户的情况,来构建AuthenticationInfo对象并返回
//通常使用的实现类为SimpleAuthenticationInfo
Object principal = instance.getInstanceName();
Object credentials = instance.getInstancePassword();
String salt = instance.getInstanceSalt();
String realmName = getName();
ByteSource credentialsSalt = ByteSource.Util.bytes(salt);
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal,credentials,credentialsSalt,realmName);
return info;
}
}
MyLoginInstance是一个接口,只是将获取名称密码和salt进行了低耦合化:
package com.tansuo365.test1.entity;
public interface MyLoginInstance {
String getInstanceName();
String getInstancePassword();
String getInstanceSalt();
}
在建立了一些不该存在的接口后,尝试着改变CustomShiroFilterFactoryBean
如我所想,实现同一个接口的实现类可能都被调用到了:
Parameter 0 of method shirFilter in com.tansuo365.test1.shiro.ShiroConfiguration required a single bean, but 2 were found:
- memberServiceImpl: defined in file [D:\新建文件夹\test1\target\classes\com\tansuo365\test1\service\impl\MemberServiceImpl.class]
- userServiceImpl: defined in file [D:\新建文件夹\test1\target\classes\com\tansuo365\test1\service\impl\UserServiceImpl.class]
所以还是去除这些多余的接口.使用老方法.
在postman中测试,admin路径可以直接调到/admin/login, 但在路径user时返回404,若直接访问/user/login则可以访问会员的登录.
这说明了在URLPathMatchingFilter中还需要根据不同的用户去获取.:
URLPathMatchingFilter中的一段:
// 如果没有登录,就跳转到登录页面
if (!subject.isAuthenticated()) {
WebUtils.issueRedirect(request, response, "/admin/login");
return false;
}
而URLPathMatchingFilter又被ShiroConfiguration拦截器所装配:
//自定义拦截器
Map<String, Filter> customisedFilter = new HashMap<>();
customisedFilter.put("url", getURLPathMatchingFilter());
在URLPathMatchingFilter中:
package com.tansuo365.test1.filter;
import com.tansuo365.test1.service.PermissionService;
import com.tansuo365.test1.util.SpringContextUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.PathMatchingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.Set;
/*URL路径匹配过滤器*/
public class URLPathMatchingFilter extends PathMatchingFilter {
@Autowired
private PermissionService permissionService;
@Override
protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue)
throws Exception {
if(null==permissionService)
permissionService = SpringContextUtils.getContext().getBean(PermissionService.class);
String requestURI = getPathWithinApplication(request);
System.out.println("requestURI:" + requestURI);
Subject subject = SecurityUtils.getSubject();
// 如果没有登录,就跳转到登录页面 --
if (!subject.isAuthenticated()) {
WebUtils.issueRedirect(request, response, "/admin/login");
return false;
}
// 看看这个路径权限里有没有维护,如果没有维护,一律放行(也可以改为一律不放行)
System.out.println("permissionService:"+permissionService);
boolean needInterceptor = permissionService.needInterceptor(requestURI);
if (!needInterceptor) {
return true;
} else {
boolean hasPermission = false;
String userName = subject.getPrincipal().toString();
Set<String> permissionUrls = permissionService.listPermissionURLs(userName);
for (String url : permissionUrls) {
// 这就表示当前用户有这个权限
if (url.equals(requestURI)) {
hasPermission = true;
break;
}
}
if (hasPermission)
return true;
else {
UnauthorizedException ex = new UnauthorizedException("当前用户没有访问路径 " + requestURI + " 的权限");
subject.getSession().setAttribute("ex", ex);
WebUtils.issueRedirect(request, response, "/admin/unauthorized");
return false;
}
}
}
}
设定的权限Service以及没有登录会跳转的路径,没有权限的提示路径都是固定的.
看张开涛大神的<跟我学Shiro>可知,在controller调用
currentUser.login(customizedToken);
后,其会自动委托给Security Manager,其负责真正的身份验证逻辑,它会委托给Authenticator进行身份验证.
接口Authenticator只有一个认证信息方法:
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.shiro.authc;
/**
* An Authenticator is responsible for authenticating accounts in an application. It
* is one of the primary entry points into the Shiro API.
* <p/>
* Although not a requirement, there is usually a single 'master' Authenticator configured for
* an application. Enabling Pluggable Authentication Module (PAM) behavior
* (Two Phase Commit, etc.) is usually achieved by the single {@code Authenticator} coordinating
* and interacting with an application-configured set of {@link org.apache.shiro.realm.Realm Realm}s.
* <p/>
* Note that most Shiro users will not interact with an {@code Authenticator} instance directly.
* Shiro's default architecture is based on an overall {@code SecurityManager} which typically
* wraps an {@code Authenticator} instance.
*
* @see org.apache.shiro.mgt.SecurityManager
* @see AbstractAuthenticator AbstractAuthenticator
* @see org.apache.shiro.authc.pam.ModularRealmAuthenticator ModularRealmAuthenticator
* @since 0.1
*/
public interface Authenticator {
/**
* Authenticates a user based on the submitted {@code AuthenticationToken}.
* <p/>
* If the authentication is successful, an {@link AuthenticationInfo} instance is returned that represents the
* user's account data relevant to Shiro. This returned object is generally used in turn to construct a
* {@code Subject} representing a more complete security-specific 'view' of an account that also allows access to
* a {@code Session}.
*
* @param authenticationToken any representation of a user's principals and credentials submitted during an
* authentication attempt.
* @return the AuthenticationInfo representing the authenticating user's account data.
* @throws AuthenticationException if there is any problem during the authentication process.
* See the specific exceptions listed below to as examples of what could happen
* in order to accurately handle these problems and to notify the user in an
* appropriate manner why the authentication attempt failed. Realize an
* implementation of this interface may or may not throw those listed or may
* throw other AuthenticationExceptions, but the list shows the most common ones.
* @see ExpiredCredentialsException
* @see IncorrectCredentialsException
* @see ExcessiveAttemptsException
* @see LockedAccountException
* @see ConcurrentAccessException
* @see UnknownAccountException
*/
public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)
throws AuthenticationException;
}
在自我实现的MyRealm中,继承了AuthorizingRealm类,而这个也继承了抽象类:
abstract class AuthenticatingRealm
而其上面的类及接口也是实现了:
public interface Realm
而该接口中,也有这个方法:
AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;
在接口Authenticator中的AuthenticationInfo返回类型的方法虽然名称为authenticate但是返回类型,传入参数类型个数,包括异常类型均是一样的,可以理解为同样的一个作用的接口都是传入AuthenticationToken,返回AuthenticationInfo.
Authenticator是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现.
Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认
ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;
Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返
回/抛出异常表示身份验证失败了。此处可以配置多个 Realm,将按照相应的顺序及策略进
行访问。
securityManager 会按照 realms 指定的顺序进行身份认证,也就是说当realm1不匹配时,则取判定后一个如realm2,
但我们的要求是前端用户权限是前端用户的realm,后端是后端的realm,应该互不干涉.
经过调整可以进行不同类型的判定
重写的MyFormAuthenticationFilter好像不太管用,虽然改了一些但是好像作用不是很大,不过扩展还是可以的.
其主要实现了将登录的form给进行一些抽象化的改变,比如原来shiro中没有loginType,而这里则可以进行设置,
如果将loginType(一个Enum枚举)列在类中自然可用,当然如果后台可以添加不同类型用户时,这个要存入数据库.
那么就可以在这里进行改善:
package com.tansuo365.test1.shiro.filter;
import com.tansuo365.test1.shiro.realm.MyAuthenticationToken;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* 如果后端可以设定其它的用户种类,那么种类名称应该存入数据库中,而不是写死在枚举类里.
* 那么就可以结合这个扩展类来进行种类的查询及比较.
* 比如在STEP 1位置判定loginType是否与数据库设定的相关.
*/
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
// public String loginType = null;
protected MyAuthenticationToken createToken(ServletRequest request, ServletResponse response) {
String username = getUsername(request);
String password = getPassword(request);
// String rememberMeParam = getRememberMeParam();
String loginType = request.getParameter("loginType");
//STEP 1:
System.err.println("loginType in MyForm:" + loginType);
System.err.println("getRememberMeBool(request):" + getRememberMeBool(request));
return new MyAuthenticationToken(username, password, getRememberMeBool(request) == null ? false : true, loginType);
}
protected String getRememberMeBool(ServletRequest request) {
return WebUtils.getCleanParam(request, DEFAULT_REMEMBER_ME_PARAM);
}
}
之后在ShiroConfiguration中进行注入:
@Bean
public FormAuthenticationFilter formAuthenticationFilter(){
return new MyFormAuthenticationFilter();
}
这个就略过了,因为现在不需要做用户种类添加的工作(如果涉及到特大型多用户网站,需要扩展性的添加角色,那么就可以将这个类进行扩展,比如LOL经常加入新英雄,要设定英雄是AD,AP还是辅助,那么就可以在这个种类中去添加了)
略过略过.
回归主题,虽然判定
MyFormAuthenticationFilter
在shiro中有个抽象类:PathMatchingFilter,其功能可以让继承的自定义类实现权限路径的拦截.
package com.tansuo365.test1.filter;
import com.tansuo365.test1.bean.MemberPermission;
import com.tansuo365.test1.service.MemberPermissionService;
import com.tansuo365.test1.service.PermissionService;
import com.tansuo365.test1.util.SpringContextUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.PathMatchingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.Set;
/*URL路径匹配过滤器*/
public class URLPathMatchingFilter extends PathMatchingFilter {
@Autowired
private PermissionService permissionService; //admin
@Autowired
private MemberPermissionService memberPermissionService; //member
/*前置处理器*/
@Override
protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue)
throws Exception {
return manyWayToLogin(request,response);
}
public boolean manyWayToLogin(ServletRequest request, ServletResponse response) throws Exception {
String requestURI = getPathWithinApplication(request);
System.out.println("requestURI:" + requestURI);
Subject subject = SecurityUtils.getSubject();
boolean st = requestURI.startsWith("/");
boolean ed = requestURI.endsWith("/");
String requestURIPure = null;
boolean needInterceptor = false;
if (st || ed) {
requestURIPure = requestURI.replace("/", "");
}
System.out.println("requestURIPure"+requestURIPure);
if (requestURIPure.contains("admin")) {
if (!subject.isAuthenticated()) {
WebUtils.issueRedirect(request, response, "/admin/login");
return false;
}
if (null == permissionService) {
permissionService = SpringContextUtils.getContext().getBean(PermissionService.class);
}
needInterceptor = permissionService.needInterceptor(requestURI);
}
if (requestURIPure.contains("user")) {
if (!subject.isAuthenticated()) {
WebUtils.issueRedirect(request, response, "/user/login");
return false;
}
if (null == permissionService) {
memberPermissionService = SpringContextUtils.getContext().getBean(MemberPermissionService.class);
}
needInterceptor = memberPermissionService.needInterceptor(requestURI);
}
return true;
// if (!needInterceptor) {
// return true;
// } else {
// boolean hasPermission = false;
// String userName = subject.getPrincipal().toString();
// Set<String> permissionUrls = permissionService.listPermissionURLs(userName);
// for (String url : permissionUrls) {
// // 这就表示当前用户有这个权限
// if (url.equals(requestURI)) {
// hasPermission = true;
// break;
// }
// }
////
// if (hasPermission)
// return true;
// else {
//
// if (requestURI.contains("admin")) {
// UnauthorizedException ex = new UnauthorizedException("当前管理员没有访问路径 " + requestURI + " 的权限");
// subject.getSession().setAttribute("ex", ex);
// WebUtils.issueRedirect(request, response, "/admin/unauthorized");
// }
// if (requestURI.contains("user")) {
// UnauthorizedException ex = new UnauthorizedException("当前会员没有访问路径 " + requestURI + " 的权限");
// subject.getSession().setAttribute("ex", ex);
// WebUtils.issueRedirect(request, response, "/user/unauthorized");
// }
//
//
// }
// return false;
// }
}
}
// // 如果没有登录,就跳转到登录页面 --
// if (!subject.isAuthenticated()) {
// if (requestURI.contains("admin")) {
//
// }
//
//
// return false;
// }
// 看看这个路径权限里有没有维护,如果没有维护,一律放行(也可以改为一律不放行)
// System.out.println("permissionService:" + permissionService);
// boolean needInterceptor = permissionService.needInterceptor(requestURI);
// if (!needInterceptor) {
// return true;
// } else {
// boolean hasPermission = false;
// String userName = subject.getPrincipal().toString();
// Set<String> permissionUrls = permissionService.listPermissionURLs(userName);
// for (String url : permissionUrls) {
// // 这就表示当前用户有这个权限
// if (url.equals(requestURI)) {
// hasPermission = true;
// break;
// }
// }
//
// if (hasPermission)
// return true;
// else {
//
//// if (requestURI.contains("admin")) {
//// UnauthorizedException ex = new UnauthorizedException("当前管理员没有访问路径 " + requestURI + " 的权限");
//// subject.getSession().setAttribute("ex", ex);
//// WebUtils.issueRedirect(request, response, "/admin/unauthorized");
//// }
//// if (requestURI.contains("user")) {
//// UnauthorizedException ex = new UnauthorizedException("当前会员没有访问路径 " + requestURI + " 的权限");
//// subject.getSession().setAttribute("ex", ex);
//// WebUtils.issueRedirect(request, response, "/user/unauthorized");
//// }
// UnauthorizedException ex = new UnauthorizedException("当前管理员没有访问路径 " + requestURI + " 的权限");
// subject.getSession().setAttribute("ex", ex);
// WebUtils.issueRedirect(request, response, "/admin/unauthorized");
//
// return false;
// }
//
// }
比如上面这个继承类,当然现在可能不用这个了,要自己重写.
package com.tansuo365.test1.shiro.filter;
import org.apache.shiro.web.filter.authc.UserFilter;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class MemberFilter extends UserFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
WebUtils.issueRedirect(request,response,"/deniedMember");
return false;
}
}
package com.tansuo365.test1.shiro.filter;
import org.apache.shiro.web.filter.authc.UserFilter;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class AdminFilter extends UserFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
WebUtils.issueRedirect(request,response,"/deniedAdmin");
return false;
}
}
重写了两种用户的UserFilter,测试否认通过路径.
UserFilter同时继承了AccessControlFilter
下面是UserFilter源码,有两个方法,一个是允许通过,一个是拒绝通过.
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.shiro.web.filter.authc;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
/**
* Filter that allows access to resources if the accessor is a known user, which is defined as
* having a known principal. This means that any user who is authenticated or remembered via a
* 'remember me' feature will be allowed access from this filter.
* <p/>
* If the accessor is not a known user, then they will be redirected to the {@link #setLoginUrl(String) loginUrl}</p>
*
* @since 0.9
*/
public class UserFilter extends AccessControlFilter {
/**
* Returns <code>true</code> if the request is a
* {@link #isLoginRequest(javax.servlet.ServletRequest, javax.servlet.ServletResponse) loginRequest} or
* if the current {@link #getSubject(javax.servlet.ServletRequest, javax.servlet.ServletResponse) subject}
* is not <code>null</code>, <code>false</code> otherwise.
*
* @return <code>true</code> if the request is a
* {@link #isLoginRequest(javax.servlet.ServletRequest, javax.servlet.ServletResponse) loginRequest} or
* if the current {@link #getSubject(javax.servlet.ServletRequest, javax.servlet.ServletResponse) subject}
* is not <code>null</code>, <code>false</code> otherwise.
*/
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (isLoginRequest(request, response)) {
return true;
} else {
Subject subject = getSubject(request, response);
// If principal is not null, then the user is known and should be allowed access.
return subject.getPrincipal() != null;
}
}
/**
* This default implementation simply calls
* {@link #saveRequestAndRedirectToLogin(javax.servlet.ServletRequest, javax.servlet.ServletResponse) saveRequestAndRedirectToLogin}
* and then immediately returns <code>false</code>, thereby preventing the chain from continuing so the redirect may
* execute.
*/
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
saveRequestAndRedirectToLogin(request, response);
return false;
}
}
之后如上图需要在ShiroConfig或ShiroConfiguration中设置这个路径到shiroFilterFactoryBean的setFilters(参数中去)
访问页面/admin 以及 /user 会走重写UserFilter的两个子类.
同时我在想,既然UserFilter也实现了PathMatchingFilter那么原来的URLPathMatchingFilter这个重写的类也基本可以用不到了.
在MemberFilter和AdminFilter中各自去定义就好了.