zoukankan      html  css  js  c++  java
  • shiro 进行权限管理 —— 用户登录认证

    本文介绍用户的认证,认证通过三个参数进行:用户名,密码和验证码。首先介绍认证的业务流程和实现方法,再介绍 shiro 的认证流程和原理,并加以实现。

    1. 认证的流程和实现

    1.1 前台发起校验的异步请求

    • 将输入的账号,密码和验证码拼接成一个字符串 code,用逗号分隔
    • 再发起一个“login_login”异步请求
    • 如果认证不通过,后台返回校验信息在前台显示
    • 如果认证通过则跳转 main/index 方法
    function severCheck(){
                if(check()){
    
                    var loginname = $("#loginname").val();
                    var password = $("#password").val();
                    var code = loginname+","+password+","+$("#code").val();
                    $.ajax({
                        type: "POST",
                        url: 'login_login',
                        data: {KEYDATA:code,tm:new Date().getTime()},
                        dataType:'json',
                        cache: false,
                        success: function(data){
                            if("success" == data.result){
                                saveCookie();
                                window.location.href="main/index";
                            }else if("usererror" == data.result){
                                $("#loginname").tips({
                                    side : 1,
                                    msg : "用户名或密码有误",
                                    bg : '#FF5080',
                                    time : 15
                                });
                                showfh();
                                $("#loginname").focus();
                            }else if("codeerror" == data.result){
                                $("#code").tips({
                                    side : 1,
                                    msg : "验证码输入有误",
                                    bg : '#FF5080',
                                    time : 15
                                });
                                showfh();
                                $("#code").focus();
                            }else{
                                $("#loginname").tips({
                                    side : 1,
                                    msg : "缺少参数",
                                    bg : '#FF5080',
                                    time : 15
                                });
                                showfh();
                                $("#loginname").focus();
                            }
                        }
                    });
                }
            }

    1.2 后台校验

    校验业务流程如下图所示

    后台校验流程图

    代码实现如下,只有最后一部分涉及 shiro 的认证,只想了解 shiro 认证直接看第二部分

    @RequestMapping(value="login_login",produces="application/json;charset=UTF-8")//接收 json 格式数据 && 编码格式为 UTF-8
        @ResponseBody
        public Object login() throws Exception{
    
            Map<String,String> map = new HashMap<String,String>();
            PageData pd = new PageData();
            pd = this.getPageData();// 获取页面参数,封装在一个 map 中,自己编写封装方法
            String errInfo = ""; // 返回消息
            String[] KEYDATA = pd.getString("KEYDATA").split(",");
            if(KEYDATA != null && KEYDATA.length == 3){
                // 获取 shiro 的 session:SecurityUtils.getSubject().getSession();
                Session session = Jurisdiction.getSession();
                String sessionCode = (String) session.getAttribute(Const.SESSION_SECURITY_CODE); //获取session 中的验证码
                String code = KEYDATA[2]; // 输入的验证码
                if(code == null || code.equals("")){
                    errInfo = "nullcode"; // 验证码为空
                }else{
                    String USERNAME = KEYDATA[0];
                    String PASSWORD = KEYDATA[1];
                    pd.put("USERNAME", USERNAME);
                    if(Tools.notEmpty(sessionCode) && sessionCode.equals(code)){ // 验证码校验
                        //密码加密
                        String passwd = new SimpleHash("SHA-1", USERNAME, PASSWORD).toString();
                        pd.put("PASSWORD", passwd);
                        pd = userService.getUserByNameAndPwd(pd);
                        if(pd != null){
                            pd.put("LAST_LOGIN",DateUtil.getTime().toString());
                            userService.updateLastLogin(pd);
                            User user = new User();
                            user.setUSER_ID(pd.getString("USER_ID"));
                            user.setUSERNAME(pd.getString("USERNAME"));
                            user.setPASSWORD(pd.getString("PASSWORD"));
                            user.setNAME(pd.getString("NAME"));
                            user.setRIGHTS(pd.getString("RIGHTS"));
                            user.setROLE_ID(pd.getString("ROLE_ID"));
                            user.setLAST_LOGIN(pd.getString("LAST_LOGIN"));
                            user.setIP(pd.getString("IP"));
                            user.setSTATUS(pd.getString("STATUS"));
                            session.setAttribute(Const.SESSION_USER, user);         //把用户信息放session中
                            session.removeAttribute(Const.SESSION_SECURITY_CODE);   //清除登录验证码的session
                            //shiro加入身份验证
                            Subject subject = SecurityUtils.getSubject(); 
                            UsernamePasswordToken token = new UsernamePasswordToken(USERNAME, PASSWORD); 
                            try { 
                                subject.login(token); 
                            } catch (AuthenticationException e) { 
                                errInfo = "身份验证失败!";
                            }
                        }else{
                            errInfo = "usererror"; //用户名或者密码错误
                        }
    
                    }else{
                        errInfo = "codeerror"; // 验证码有误
                    }
                    if(Tools.isEmpty(errInfo)){
                        errInfo = "success";                    //验证成功
                    }
                }
            }else{
                errInfo = "error";  //缺少参数
            }
    
            map.put("result", errInfo);
            return AppUtil.returnObject(new PageData(), map);
        }

    2. shiro 实现认证

    上文认证流程,只有最后部分利用 shiro 进行身份认证

    //shiro加入身份验证
                            Subject subject = SecurityUtils.getSubject(); 
                            UsernamePasswordToken token = new UsernamePasswordToken(USERNAME, PASSWORD); 
                            try { 
                                subject.login(token); 
                            } catch (AuthenticationException e) { 
                                errInfo = "身份验证失败!";
                            }

    2.1 shiro 认证流程

    shiro 认证流程图

    1、应用程序构建了一个终端用户认证信息的AuthenticationToken 实例后,调用Subject.login方法。
    
    2、Subject的实例通常是DelegatingSubject类(或子类)的实例对象,在认证开始时,会委托应用程序设置的securityManager实例调用securityManager.login(token)方法。 
    
    3、SecurityManager接受到token(令牌)信息后会委托内置的Authenticator的实例(通常都是ModularRealmAuthenticator类的实例)调用authenticator.authenticate(token). ModularRealmAuthenticator在认证过程中会对设置的一个或多个Realm实例进行适配,它实际上为Shiro提供了一个可拔插的认证机制。 
    
    4、如果在应用程序中配置了多个Realm,ModularRealmAuthenticator会根据配置的AuthenticationStrategy(认证策略)来进行多Realm的认证过程。在Realm被调用后,AuthenticationStrategy将对每一个Realm的结果作出响应。 注:如果应用程序中仅配置了一个Realm,Realm将被直接调用而无需再配置认证策略。 
    
    5、判断每一个Realm是否支持提交的token,如果支持,Realm将调用getAuthenticationInfo(token); getAuthenticationInfo 方法就是实际认证处理,我们通过覆盖Realm的doGetAuthenticationInfo方法来编写我们自定义的认证处理。
    

    所以我们需要做两件事:
    1. 添加自定义realm
    2. 编写自定义 realm

    2.2 配置文件添加

    <!-- 自定义 realm 用于校验 -->
        <bean id="shiroRealm" class="com.shuiyujie.interceptor.shiro.ShiroRealm" />

    2.3 自定义 realm 中的认证方法

    /*
         * 登录信息和用户验证信息验证(non-Javadoc)
         * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
             String username = (String)token.getPrincipal();                //得到用户名 
             String password = new String((char[])token.getCredentials());  //得到密码
             if(null != username && null != password){
                 return new SimpleAuthenticationInfo(username, password, getName());
             }else{
                 return null;
             }
        }

    2.4 用户退出

    @RequestMapping(value="/logout")
        public ModelAndView logout(){
            String USERNAME = Jurisdiction.getUsername();   //当前登录的用户名
            logBefore(logger, USERNAME+"退出系统");
            ModelAndView mv = this.getModelAndView();
            PageData pd = new PageData();
            Session session = Jurisdiction.getSession();    //以下清除session缓存
            session.removeAttribute(Const.SESSION_USER);
                    .
                    .
                    .
            //shiro销毁登录
            Subject subject = SecurityUtils.getSubject(); 
            subject.logout();
    
            // 跳转回登录界面
            mv.setViewName("system/index/login");
            mv.addObject("pd",pd);
            return mv;
        }

    还差个认证成功跳转的方法,“/main/index”

    -略-

  • 相关阅读:
    Aurora 数据库支持多达五个跨区域只读副本
    Amazon RDS 的 Oracle 只读副本
    Amazon EC2 密钥对
    DynamoDB 读取请求单位和写入请求单位
    使用 EBS 优化的实例或 10 Gb 网络实例
    启动 LAMP 堆栈 Web 应用程序
    AWS 中的错误重试和指数退避 Error Retries and Exponential Backoff in AWS
    使用 Amazon S3 阻止公有访问
    路由表 Router Table
    使用MySQLAdmin工具查看QPS
  • 原文地址:https://www.cnblogs.com/shuiyj/p/13185238.html
Copyright © 2011-2022 走看看