zoukankan      html  css  js  c++  java
  • Shiro登录中遇到了问题

    Shiro登录中遇到了问题

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

    问题

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

    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拦截处理.   

    判断是否已通过验证

    // 获取到当前subject
    Subject subject = SecurityUtils.getSubject();
    // 判断是否已通过验证或者已经记住
    if (subject.isAuthenticated()|| subject.isRemembered()) {
        ...
    }

     

    问题1解决方式

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

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

    问题2解决方式

    原因

    因为登陆后Cookie里面设置了域名,如果当前访问的域名和这个Cookie的域名不一致,将无法登陆成功。

    但是域名和IP本地做了映射,并且在系统中做了配置,应该不是因为域名和IP而导致的创建了不同的session,更何况session的唯一标示是Jsessionid。

    但是cookie是根据域名匹配的,如果域名被修改,那么就会导致后台拿不到JSESSIONID,也就会显示当前未登录。

    而现在系统的部署,域名是虚拟生成的,任何访问的客户端,本地域名都不会被看到,推断是因为这个原因导致。

  • 相关阅读:
    Python 类中方法的内部变量,命名加'self.'变成 self.xxx 和不加直接 xxx 的区别
    用foreach遍历 datagridView 指定列所有的内容
    treeView1.SelectedNode.Level
    YES NO 上一个 下一个
    正则 单词全字匹配查找 reg 边界查找 精确匹配 只匹配字符 不含连续的字符
    抓取2个字符串中间的字符串
    sqlite 60000行 插入到数据库只用不到2秒
    将多行文本以单行的格式保存起来 读和写 ini
    将秒转换成时间格式
    richtextbox Ctrl+V只粘贴纯文本格式
  • 原文地址:https://www.cnblogs.com/embraceU/p/9265790.html
Copyright © 2011-2022 走看看