基于权限安全框架Shiro的登录验证功能实现

|▌冷眼眸甩不掉的悲伤 提交于 2020-08-15 04:08:21

目前在企业级项目里做权限安全方面喜欢使用Apache开源的Shiro框架或者Spring框架的子框架Spring Security。

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。

Shiro框架具有轻便,开源的优点,所以本博客介绍基于Shiro的登录验证实现。

在maven里加入shiro需要的jar

    <!--shiro start-->
          <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-all</artifactId>
                <version>1.2.3</version>
           </dependency>
    <!-- shiro end-->

在web.xml加上Shiro过滤器配置:


      <!-- Shiro过滤器配置 start -->
       <filter>
         <filter-name>shiroFilter</filter-name>
         <filter-class>
                 org.springframework.web.filter.DelegatingFilterProxy
             </filter-class>
         <init-param>
           <param-name>targetFilterLifecycle</param-name>
           <param-value>true</param-value>
         </init-param>
       </filter>
       <filter-mapping>
         <filter-name>shiroFilter</filter-name>
         <url-pattern>/*</url-pattern>
       </filter-mapping>
      <!-- Shiro过滤器配置 end -->

编写shiro的ShiroRealm类:

    package org.muses.jeeplatform.core.security.shiro;

    import javax.annotation.Resource;

    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.LockedAccountException;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authc.UnknownAccountException;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.muses.jeeplatform.model.entity.User;
    import org.muses.jeeplatform.service.UserService;

    /**
     * @description 基于Shiro框架的权限安全认证和授权
     * @author Nicky
     * @date 2017年3月12日
     */
    public class ShiroRealm extends AuthorizingRealm {

        /**注解引入业务类**/
        @Resource
        UserService userService;

        /**
         * 登录信息和用户验证信息验证(non-Javadoc)
         * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(AuthenticationToken)
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

             String username = (String)token.getPrincipal();                //得到用户名 
             String password = new String((char[])token.getCredentials());  //得到密码

             User user = userService.findByUsername(username);

             /**检测是否有此用户 **/
             if(user == null){
                 throw new UnknownAccountException();//没有找到账号异常
             }
             /**检验账号是否被锁定 **/
             if(Boolean.TRUE.equals(user.getLocked())){
                 throw new LockedAccountException();//抛出账号锁定异常
             }
             /**AuthenticatingRealm使用CredentialsMatcher进行密码匹配**/
             if(null != username && null != password){
                 return new SimpleAuthenticationInfo(username, password, getName());
             }else{
                 return null;
             }

        }

        /**
         * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法(non-Javadoc)
         * @see AuthorizingRealm#doGetAuthorizationInfo(PrincipalCollection)
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
            String username = (String)pc.getPrimaryPrincipal();
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
            authorizationInfo.setRoles(userService.getRoles(username));
            authorizationInfo.setStringPermissions(userService.getPermissions(username));
            System.out.println("Shiro授权");
            return authorizationInfo;
        }

         @Override
         public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
             super.clearCachedAuthorizationInfo(principals);
         }

         @Override
         public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
             super.clearCachedAuthenticationInfo(principals);
         }

         @Override
         public void clearCache(PrincipalCollection principals) {
              super.clearCache(principals);
         }

    }

在Spring框架里集成Shiro,加入配置

    <!--  Shiro start  -->
            <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
                <property name="realm" ref="ShiroRealm" />
            </bean>

            <!-- 项目自定义的Realm -->
            <bean id="ShiroRealm" class="org.muses.jeeplatform.core.security.shiro.ShiroRealm" ></bean>

            <!-- Shiro Filter -->
            <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
                <property name="securityManager" ref="securityManager" />

                <property name="loginUrl" value="/login" />

                <property name="successUrl" value="/admin/index" />

                <property name="unauthorizedUrl" value="/login" />

                <property name="filterChainDefinitions">
                    <value>
                    /static/**                  = anon
                    /upload/**                  = anon
                    /plugins/**                 = anon
                    /code                       = anon
                    /login                      = anon
                    /logincheck                 = anon
                    /**                         = authc
                    </value>
                </property>
            </bean>
        <!--  Shiro end  -->  

登录验证控制类实现:

    package org.muses.jeeplatform.web.controller;

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;

    import javax.servlet.http.HttpServletRequest;

    import net.sf.json.JSONArray;
    import net.sf.json.JSONObject;

    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.crypto.hash.SimpleHash;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.muses.jeeplatform.core.Constants;
    import org.muses.jeeplatform.model.entity.Menu;
    import org.muses.jeeplatform.model.entity.Permission;
    import org.muses.jeeplatform.model.entity.Role;
    import org.muses.jeeplatform.model.entity.User;
    import org.muses.jeeplatform.service.MenuService;
    import org.muses.jeeplatform.service.UserService;
    import org.muses.jeeplatform.utils.Tools;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.servlet.ModelAndView;

    /**
     * @description 登录操作的控制类,使用Shiro框架,做好了登录的权限安全认证,
     * getRemortIP()方法获取用户登录时的ip并保存到数据库
     * @author Nicky
     * @date 2017年3月15日
     */
    @Controller
    public class LoginController extends BaseController {

        @Autowired
        UserService userService;
        @Autowired
        MenuService menuService;

        /**
         * 获取登录用户的IP
         * @throws Exception 
         */
        public void getRemortIP(String username)  {  
            HttpServletRequest request = this.getRequest();
            Map<String,String> map = new HashMap<String,String>();
            String ip = "";
            if (request.getHeader("x-forwarded-for") == null) {  
                ip = request.getRemoteAddr();  
            }else{
                ip = request.getHeader("x-forwarded-for");  
            }
            map.put("username", username);
            map.put("loginIp", ip);
             userService.saveIP(map);
        }  

        /**
         * 访问后台登录页面
         * @return
         * @throws Exception
         */
        @RequestMapping(value="/login",produces="text/html;charset=UTF-8")
        public ModelAndView toLogin()throws ClassNotFoundException{
            ModelAndView mv = this.getModelAndView();
            mv.setViewName("admin/frame/login");
            return mv;
        }

        /**
         * 基于Shiro框架的登录验证,页面发送JSON请求数据,
         * 服务端进行登录验证之后,返回Json响应数据,"success"表示验证成功
         * @param request
         * @return
         * @throws Exception
         */
        @RequestMapping(value="/logincheck", produces="application/json;charset=UTF-8")
        @ResponseBody
        public String loginCheck(HttpServletRequest request)throws AuthenticationException{
            JSONObject obj = new JSONObject();
            String errInfo = "";//错误信息
            String logindata[] = request.getParameter("LOGINDATA").split(",");
            if(logindata != null && logindata.length == 3){
                //获取Shiro管理的Session
                Subject subject = SecurityUtils.getSubject();
                Session session = subject.getSession();
                String codeSession = (String)session.getAttribute(Constants.SESSION_SECURITY_CODE);
                String code = logindata[2]; 
                /**检测页面验证码是否为空,调用工具类检测**/
                if(Tools.isEmpty(code)){
                    errInfo = "nullcode";
                }else{
                    String username = logindata[0];
                    String password = logindata[1];
                    if(Tools.isNotEmpty(codeSession) && codeSession.equalsIgnoreCase(code)){
                        //Shiro框架SHA加密
                        String passwordsha = new SimpleHash("SHA-1",username,password).toString();
                        System.out.println(passwordsha);
                        //检测用户名和密码是否正确
                        User user = userService.doLoginCheck(username,passwordsha);
                        if(user != null){
                            if(Boolean.TRUE.equals(user.getLocked())){
                                errInfo = "locked";
                            }else{
                                //Shiro添加会话
                                session.setAttribute("username", username);
                                session.setAttribute(Constants.SESSION_USER, user);
                                //删除验证码Session
                                session.removeAttribute(Constants.SESSION_SECURITY_CODE);
                                //保存登录IP
                                getRemortIP(username);
                                /**Shiro加入身份验证**/
                                Subject sub = SecurityUtils.getSubject();
                                UsernamePasswordToken token = new UsernamePasswordToken(username,password);
                                sub.login(token);
                            }
                        }else{
                            //账号或者密码错误
                            errInfo = "uerror";
                        }
                        if(Tools.isEmpty(errInfo)){
                            errInfo = "success";
                        }
                    }else{
                        //缺少参数
                        errInfo="codeerror";
                    }
                }
            }
            obj.put("result", errInfo);
            return obj.toString();
        }

        /**
         * 后台管理系统主页
         * @return
         * @throws Exception
         */
        @RequestMapping(value="/admin/index")
        public ModelAndView toMain() throws AuthenticationException{
            ModelAndView mv = this.getModelAndView();
            /**获取Shiro管理的Session**/
            Subject subject = SecurityUtils.getSubject();
            Session session = subject.getSession();
            User user = (User)session.getAttribute(Constants.SESSION_USER);

            if(user != null){
                ...//业务实现
            }else{
                //会话失效,返回登录界面
                mv.setViewName("admin/frame/login");
            }
            mv.setViewName("admin/frame/index");
            return mv;
        }

        /**
         * 注销登录
         * @return
         */
        @RequestMapping(value="/logout")
        public ModelAndView logout(){
            ModelAndView mv = this.getModelAndView();
            /**Shiro管理Session**/
            Subject sub = SecurityUtils.getSubject();
            Session session = sub.getSession();
            session.removeAttribute(Constants.SESSION_USER);
            session.removeAttribute(Constants.SESSION_SECURITY_CODE);
            /**Shiro销毁登录**/
            Subject subject = SecurityUtils.getSubject();
            subject.logout();
            /**返回后台系统登录界面**/
            mv.setViewName("admin/frame/login");
            return mv;
        }

    }

前端Ajax和JQeury校验实现:

     /**客户端校验**/
        function checkValidity() {

            if ($("#username").val() == "") {

                $("#username").tips({
                    side : 2,
                    msg : '用户名不得为空',
                    bg : '#AE81FF',
                    time : 3
                });

                $("#username").focus();
                return false;
            }

            if ($("#password").val() == "") {
                $("#password").tips({
                    side : 2,
                    msg : '密码不得为空',
                    bg : '#AE81FF',
                    time : 3
                });

                $("#password").focus();
                return false;
            }
            if ($("#code").val() == "") {

                $("#code").tips({
                    side : 1,
                    msg : '验证码不得为空',
                    bg : '#AE81FF',
                    time : 3
                });

                $("#code").focus();
                return false;
            }

            return true;
        }

        /**服务器校验**/
        function loginCheck(){
            if(checkValidity()){
                var username = $("#username").val();
                var password = $("#password").val();
                var code = username+","+password+","+$("#code").val();
                $.ajax({
                    type: "POST",//请求方式为POST
                    url: 'logincheck',//检验url
                    data: {LOGINDATA:code,tm:new Date().getTime()},//请求数据
                    dataType:'json',//数据类型为JSON类型
                    cache: false,//关闭缓存
                    success: function(data){//响应成功
                        if("success" == data.result){
                            $("#login").tips({
                                side : 1,
                                msg : '正在登录 , 请稍后 ...',
                                bg : '#68B500',
                                time : 10
                            });
                            window.location.href="admin/index";
                        }else if("uerror" == data.result){
                            $("#username").tips({
                                side : 1,
                                msg : "用户名或密码有误",
                                bg : '#FF5080',
                                time : 15
                            });
                            $("#username").focus();
                        }else if("codeerror" == data.result){
                            $("#code").tips({
                                side : 1,
                                msg : "验证码输入有误",
                                bg : '#FF5080',
                                time : 15
                            });
                            $("#code").focus();
                        }else if("locked" == data.result){
                            alert('您的账号被锁定了,呜呜');
                        }else{
                            $("#username").tips({
                                side : 1,
                                msg : "缺少参数",
                                bg : '#FF5080',
                                time : 15
                            });
                            $("#username").focus();
                        }
                    }
                });
            }
        }

2020080100170\_1.png

登录成功,Session会话过期,需要重新登录,保证系统安全性
2020080100170\_2.png

本博客只提供基于Shiro的登录验证实现,具体代码可以去我的github下载:https://github.com/u014427391/jeeplatform
欢迎star


来源:https://www.cnblogs.com/mzq123

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!