• shiro-登录验证


    shiro实现登录验证,可以用它自身的方法来实现,也可以自定义方法来实现登录验证,了解了shiro的登录逻辑,实现自定义的验证逻辑就很简单

    1、用shiro方法实现

    shiro配置:

    
            
            
            
            
    
                
                    /static/**=anon
                    /login=authc
                    /logout=logout
                    /unauthorize=authc
                    /**=user,perms
                
            
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    由于shiro默认注册了FormAuthenticationFilter,所以配置中可以不需要为此方法定义bean,但有个前提,登录页面中的登录账号和密码,记住我的name必须和FormAuthenticationFilter默认的名称一致,如下图

    如果登录页面的name和FormAuthenticationFilter不一致,则需要自己为FormAuthenticationFilter进行配置

    
            
            
            
            
            
    
    
            
            
            
            
    
                
                    
                
            
    
                
                    /static/**=anon
                    /login=authc
                    /logout=logout
                    /unauthorize=authc
                    /**=user,perms <
                
            
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    登录页面提交后,跳转到 /login,进入登录方法,由于此路径权限设置为authc,shiro对该路径进行过滤,authc权限由FormAuthenticationFilter进行过滤。登录请求进入onAccessDenied方法

    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
            if (isLoginRequest(request, response)) {  //判断是否是登录请求
                if (isLoginSubmission(request, response)) { // 是否是http post请求
                    if (log.isTraceEnabled()) {
                        log.trace("Login submission detected.  Attempting to execute login.");
                    }
                    return executeLogin(request, response);
                } else {
                    if (log.isTraceEnabled()) {
                        log.trace("Login page view.");
                    }
                    //allow them to see the login page ;)
                    return true;
                }
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("Attempting to access a path which requires authentication.  Forwarding to the " +
                            "Authentication url [" + getLoginUrl() + "]");
                }
                saveRequestAndRedirectToLogin(request, response);
                return false;
            }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    其中 executeLogin(request, response)方法的具体实现在继承的AuthenticatingFilter里

    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);
            }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    剖析:createToken(request, response); 具体实现在子类FormAuthenticationFilter中

    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        String username = getUsername(request);
        String password = getPassword(request);
        return createToken(username, password, request, response);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    从上可以看出,具体的登录账号和密码从request中取出来,并创建了token对象,调用subject的login方法,login方法实现大致流程是用token去realm中取AuthenticationInfo对象,AuthenticationInfo对象存放的是正确的登录账号和密码,并和token中数据进行匹配,然后根据匹配情况返回相应的结果。realm中方法需自己实现,大致流程:从token中取出用户登录填写的账号,去查找正确的登录信息,若是查找不到,返回null,如果查找到对应的登录账号和密码,则封装到AuthenticationInfo对象中,并返回该对象。

    java代码:

    @RequestMapping(value = "/login")
    public String toLogin(HttpServletRequest request, Model model){
            String exceptionClassName = (String)request.getAttribute("shiroLoginFailure");
            String error = null;
            if(UnknownAccountException.class.getName().equals(exceptionClassName)) {
                error = "用户名/密码错误";
            } else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
                error = "用户名/密码错误";
            } else if(exceptionClassName != null) {
                error = "其他错误:" + exceptionClassName;
            }
            model.addAttribute("error", error);
            return "login";  // 跳转到登录页面
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2、自定义登录实现

    最关键的是在shiro配置中,登录提交的url需设置为不过滤处理,如提交到/login,则/login=anon,

    方法中可实现自己的登录验证逻辑。若是账号密码匹配工作仍要shiro来完成,则将用户填写的账号和密码封装到token对象中,调用subject的login方法。

    代码示例:

    UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
           try {
                subject.login(token);
            } catch (UnknownAccountException ua){
                returnInfo.setMessage("用户名错误");
            } catch (IncorrectCredentialsException ic){
                returnInfo.setMessage("密码错误");
            }
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    附:

    1)shiro默认注册的filters

    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);
    }

    2)登录异常

    UnknownAccountException(账号不存在)
    IncorrectCredentialsException(密码错误)
    DisabledAccountException(帐号被禁用)
    LockedAccountException(帐号被锁定)
    ExcessiveAttemptsException(登录失败次数过多)
    ExpiredCredentialsException(凭证过期)等

  • 相关阅读:
    每日一题——将一个正整数分解质因数
    【TCP/IP】网络模型
    微信小程序测试策略和注意事项?
    【数据结构初阶】顺序循环队列和链式循环队列
    如何使用Jest生成中文测试报告
    C语言-调试文件
    【杂七杂八】Huawei Gt runner手表系统降级
    kettle安装使用与部署
    [杂记]关于C++中类继承的一些理解
    java代码:Random和Scanner应用的小例子-猜数字小游戏
  • 原文地址:https://blog.csdn.net/segegefe/article/details/126496214