Apache Shiro 具有两种认证方式:①Filter 认证方式;②注解认证方式:在需要认证的方法上添加注解 @RequiresAuthentication、@RequiresPermissions、@RequiresRoles 等等
1.针对 Filter 的认证方式
Apache Shiro 默认配置了 11 个 Filter,分别是
public enum DefaultFilter {
anon(AnonymousFilter.class),
authc(FormAuthenticationFilter.class),
authcBasic(BasicHttpAuthenticationFilter.class),
logout(LogoutFilter.class),
noSessionCreation(NoSessionCreationFilter.class),
perms(PermissionsAuthorizationFilter.class),
port(PortFilter.class),
rest(HttpMethodPermissionFilter.class),
roles(RolesAuthorizationFilter.class),
ssl(SslFilter.class),
user(UserFilter.class);
private final Class<? extends Filter> filterClass;
private DefaultFilter(Class<? extends Filter> filterClass) {
this.filterClass = filterClass;
}
//......
}
- 可以对其核心的几个 Filter 进行改写,以实现自己的需求
- authc,使得在用户未认证时返回 JSON 信息
- logout,使得在注销时返回 JSON(有BUG,一旦配置好,所有请求都会进入此Filter,即所有请求必会进行注销,放弃之)
- perms,使得在非法用户权限时返回 JSON
- roles,使得在非法用户角色时返回 JSON
项目相关参数
@Component
@ConfigurationProperties(prefix="project")
public class Project {
//未登录
public static final Integer NO_LOGIN_CODE = 1;
public static final String NO_LOGIN_MESSAGE = "请先进行登录";
//登录成功
public static final Integer LOGIN_SUCCESS_CODE = 2;
public static final String LOGIN_SUCCESS_MESSAGE = "登录成功";
//登录失败
public static final Integer LOGIN_FAILURE_CODE = 3;
public static final String LOGIN_FAILURE_MESSAGE = "登录失败";
//注销
public static final Integer LOGOUT_CODE = 4;
public static final String LOGOUT_MESSAGE = "注销成功";
//缺少用户权限
public static final Integer NO_AUTH_CODE = 5;
public static final String NO_AUTH_MESSAGE = "权限不足";
//缺少用户角色
public static final Integer NO_ROLE_CODE = 6;
public static final String NO_ROLE_MESSAGE = "用户角色不符合";
public static HttpServletResponse servletResponseProcess(ServletResponse response){
HttpServletResponse res = (HttpServletResponse)response;
res.setContentType("application/json;charset=UTF-8");
return res;
}
}
由于 Apache Shiro 框架的灵活性,我们可以自定义派生类来继承这几个 Filter,并重写相关方法以实现自定义的 Restful 逻辑
1.authc,继承 FormAuthenticationFilter.class
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
//在用户未认证时调用
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
// return super.onAccessDenied(request, response, mappedValue);
HttpServletResponse res = Project.servletResponseProcess(response);
//输出 JSON 字符串
res.getWriter().print(
JSON.toJSON(
Result.returnError(Project.NO_LOGIN_CODE, Project.NO_LOGIN_MESSAGE)));
//中止请求,不再传给下一个Filter
return false;
}
}
2.logout,继承 LogoutFilter.class(配置之后出现了莫名其妙的问题,不推荐使用,建议编写代码 Subject.logout(); 手动注销)
3.perms,继承 PermissionsAuthorizationFilter.class
public class MyPermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {
//在非法用户权限时调用
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
// return super.onAccessDenied(request, response, mappedValue);
//获取被要求的权限
String[] perms = (String[]) mappedValue;
HttpServletResponse res = Project.servletResponseProcess(response);
//输出 JSON 字符串
res.getWriter().print(
JSON.toJSON(
Result.returnError(Project.NO_AUTH_CODE,
Project.NO_AUTH_MESSAGE + Arrays.toString(perms))));
//中止请求,不再传给下一个Filter
return false;
}
}
4.roles,继承 RolesAuthorizationFilter.class
public class MyRolesAuthorizationFilter extends RolesAuthorizationFilter {
//在非法用户角色时调用
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
// return super.onAccessDenied(request, response, mappedValue);
//获取被要求的角色
String[] rolesArray = (String[]) mappedValue;
HttpServletResponse res = Project.servletResponseProcess(response);
//输出 JSON 字符串
res.getWriter().print(
JSON.toJSON(
Result.returnError(Project.NO_ROLE_CODE,
Project.NO_ROLE_MESSAGE + Arrays.toString(rolesArray))));
//中止请求,不再传给下一个Filter
return false;
}
}
最后一步,配置这几个自定义类,以覆盖默认配置,从而使得我们的代码生效(编辑Spring配置applicationContext.xml)
<!--Shiro 核心过滤器/拦截器,拦截一切请求-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="filters">
<util:map>
<entry key="authc" value-ref="myFormAuthenticationFilter"/>
<entry key="perms" value-ref="myPermissionsAuthorizationFilter"/>
<entry key="roles" value-ref="myRolesAuthorizationFilter"/>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
# some example chain definitions:
#/admin/** = authc, roles[admin]
#/docs/** = authc, perms[document:read]
#/** = authc
# more URL-to-FilterChain definitions here
#(从上往下匹配,局部配置放顶部,全局配置放底部)
/0/** = authc
/1/** = roles[admin]
/2/** = perms[user:delete]
/3/** = perms[user:delete,user:manage,user:hello]
/** = anon
</value>
</property>
</bean>
<!--自定义的 Filter 派生类-->
<bean id="myFormAuthenticationFilter" class="com.cstor.safe.config.shiro.MyFormAuthenticationFilter"/>
<bean id="myPermissionsAuthorizationFilter" class="com.cstor.safe.config.shiro.MyPermissionsAuthorizationFilter"/>
<bean id="myRolesAuthorizationFilter" class="com.cstor.safe.config.shiro.MyRolesAuthorizationFilter"/>
打开浏览器,依次访问网站的URL: /0、/1、/2、/3,得到内容为
{"errorCode":"1","resultFlag":false,"message":"请先进行登录"}
{"errorCode":"6","resultFlag":false,"message":"用户角色不符合[admin]"}
{"errorCode":"5","resultFlag":false,"message":"权限不足[user:delete]"}
{"errorCode":"5","resultFlag":false,"message":"权限不足[user:delete, user:manage, user:hello]"}
2.针对注解的认证方式
- 对于 @RequiresAuthentication、@RequiresPermissions、@RequiresRoles 等相关注解,Shiro 框架并未提供开发者介入的机会,一旦认证不通过,就会在注解方法处抛出异常(可以采用全局异常处理器,进行统一处理)
- @RequiresAuthentication,如果用户未登录,抛出异常 UnauthenticatedException
- @RequiresRoles,如果用户角色不符合,抛出异常 UnauthorizedException
- @RequiresPermissions,如果用户权限不符合,抛出异常 UnauthorizedException