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,也就会显示当前未登录。

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

  • 相关阅读:
    C#屏幕截图
    WPF转换器用法示例
    WPF自定义数字输入框控件
    LINQ 用法,返回结果不是在定义时取值,而是在调用时实时取值,有意思!
    类泛型--必须继承接口
    wpf在异步中给前台赋值
    VS Code中Matlab插件安装设置
    Python 自动给数字前面补0
    Tensorflow (1)
    解决Keras在IDE集成环境中找不到nvcc
  • 原文地址:https://www.cnblogs.com/embraceU/p/9265790.html
Copyright © 2011-2022 走看看