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”

-略-

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值