Shiro登录中遇到了问题

Shiro登录中遇到了问题

记录二次开发中遇到的问题, 如果系统学习Shiro, 推荐跟我学Shrio

问题

  1. 项目是要将验证从本地改为LDAP验证, 但是因为jeecms的验证和授权中, 用户和角色以及权限的信息都来自本地, 大量的改造不适合.
  2. 域名的拦截, 还在排查问题所在

HTTP 302

HTTP 302 Found 重定向状态码表明请求的资源被暂时的移动到了由Location 头部指定的 URL 上。就是请求的资源被重定向到了新的地址。

要查看Filter是否对关键词路径进行了拦截。

Shiro

项目中用的jeecms, 安全验证用的shiro,架构是Spring+SpringMVC,查看了一下web.xml,有关于shiro的filter,用的DelegatingFilterProxy类做代理,在SpringContext中配置Shiro的bean。

DelegatingFilterProxy

是对于servlet filter的代理,通过spring容器管理filter的生命周期,可以通过Spring容器注入需要的bean,以及读取需要的配置文件。

【web.xml】

<filter>
    <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>

【shiroContext.xml】

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
</bean>

拦截器链

  1. 先执行Shiro自己的Filter链
  2. 执行Servlet的Filter链

自定义拦截器

  1. 可以根据自己的需求扩展拦截器,继承要扩展的拦截器
  2. 对相应的方法进行扩展
  3. 在配置文件中修改对应的Filter类名
// 代码参考<跟我学shiro>
public class MyAccessControlFilter extends AccessControlFilter { protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { System.out.println("access allowed"); return true; } protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { System.out.println("访问拒绝也不自己处理,继续拦截器链的执行"); return true; } }
[filters]  
myFilter4=com.github.zhangkaitao.shiro.chapter8.web.filter.MyAccessControlFilter  
[urls]  
/** 代码参考<跟我学shiro>

执行过程

  1. ShiroFilter提供Shiro的入口
  2. AdviceFilter提供AOP的风格,分为preHandler,postHandle,afterCompletion三个方法针对AOP的前后增强(预处理和进行后处理)
  3. PathMatchingFilter匹配ANT风格的请求路径,解析拦截器参数
    1. onPreHandle方法将路径绑定参数配置传给mappedValue,然后进行一些验证
  4. AccessControlFilter访问控制功能,如是否允许访问,拒绝后如何处理等
    1. isAccessAllowed表示是否允许访问
    2. onAccessDenied表示当拒绝访问时是否处理了
  5. 如果扩展访问控制可以继承AccessControlFilter,如果添加通用数据可以继承PathMatchingFilter

DelegatingSubject

shiro通过FormAuthenticationFilter来进行表单验证,如果在验证前要进行其他比如验证码的验证,可以自定义一个继承的子类.扩展对应的方法.

这里是对访问控制的扩展,继承关系为:FormAuthenticationFilter --> AuthenticatingFilter --> AuthenticationFilter --> AccessControlFilter.

可以在扩展的拦截器中重写executeLogin方法,在里面扩展对访问的控制

// 源码
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        AuthenticationToken token = createToken(request, response);
        if (token == null) {
            String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +
                    "must be created in order to execute a login attempt.";
            throw new IllegalStateException(msg);
        }
        try { Subject subject = getSubject(request, response); subject.login(token); return onLoginSuccess(token, subject, request, response); } catch (AuthenticationException e) { return onLoginFailure(token, e, request, response); } }

这里的执行是获取当前的subject --> 根据请求创建token -->执行subject.login()方法 --> 返回成功或者失败的处理

login()方法执行中,会进行一系列的处理,首先是交给DelegatingSubject,后面的没有详细研究,最后调用realm的doGetAuthenticationInfo方法获取身份验证的相关信息,通过SimpleAuthenticationInf返回AuthenticationInfo.

SimpleAuthenticationInfo

用来返回AuthenticationInfo信息,而AuthenticationInfo有两个作用

  1. 如果Realm是AuthenticatingRealm子类,则提供给AuthenticatingRealm内部使用的CredentialsMatcher进行凭据验证
  2. 提供给SecurityManager来创建Subject(提供身份信息)

credentialsMatcher

protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
        CredentialsMatcher cm = getCredentialsMatcher();
        if (cm != null) {
            if (!cm.doCredentialsMatch(token, info)) {
                //not successful - throw an exception to indicate this:
                String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
                throw new IncorrectCredentialsException(msg); } } else { throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " + "credentials during authentication. If you do not wish for credentials to be examined, you " + "can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance."); } }

CredentialsMatcher实现了token和info的凭证验证.

授权问题

关于授权, 一开始进入了一个误区, 认为在通过验证之后会走授权的部分, 当页面出现授权问题的时候, 发现打过的断点没有进去, 仔细看了下开涛大神的讲解,  是当要访问对应的资源时, 才会验证当前用户的授权情况. 

如果想对资源设置权限, 可以应用Spring的AOP注解, 在对应的AnnotationController控制器(你的Controller)上加@RequiresRoles("admin"), 如果抛出异常, 可以用Spring的ExceptionHandler拦截处理.   

问题1解决方式

不改造本地验证和授权方式, 让ldap用户落地, 但是不包括密码, 每次根据user和password去判断验证的方式和是否新增用户.

在继承了AuthorizingRealm的CmsAuthorizingRealm类中修改doGetAuthenticationInfo方法即可.

猜你喜欢

转载自www.cnblogs.com/embraceU/p/9265790.html