• 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”

    -略-

  • 相关阅读:
    写一个迷你版Smarty模板引擎,对认识模板引擎原理非常好(附代码)
    工作1个月+1个星期
    矫正骨盆前倾!平坦小腹!解决腰痛!
    《非暴力沟通》
    了不起的盖茨比
    X战警系列
    Docker常用命令大全
    学习笔记12
    电子公文传输系统1个人贡献
    实验四 Web服务器2
  • 原文地址:https://www.cnblogs.com/shuiyj/p/13185238.html
Copyright © 2020-2023  润新知