• 在elementui和MyBatis中前后端分离中使用shiro


    这是单个项目中的集成:https://www.cnblogs.com/xiaoruirui/p/11696318.html

    登陆页面

    <template>
      <div class="login-container">
        <el-form :model="ruleForm2" :rules="rules2"
                 status-icon
                 ref="ruleForm2"
                 label-position="left"
                 label-width="0px"
                 class="demo-ruleForm login-page">
          <h3 class="title">系统登录</h3>
          <el-form-item prop="username">
            <el-input type="username"
                      v-model="ruleForm2.username"
                      auto-complete="off"
                      placeholder="用户名"
            ></el-input>
          </el-form-item>
          <el-form-item prop="password">
            <el-input type="password"
                      v-model="ruleForm2.password"
                      auto-complete="off"
                      placeholder="密码"
            ></el-input>
          </el-form-item>
          <el-checkbox
                  v-model="checked"
                  class="rememberme"
          >记住密码</el-checkbox>
          <el-form-item style="100%;">
            <el-button type="primary" style="100%;" @click="handleSubmit" :loading="logining">登录</el-button>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="getWxLoginUrl">微信登陆</el-button>
          </el-form-item>
    
        </el-form>
      </div>
    </template>
    
    <script>
        export default {
            data(){
                return {
                    addFormVisible:false,
                    logining: false,
                    ruleForm2: {
                        username: '',
                        password: '',
                    },
                    wxLoginUrl:"",
                    rules2: {
                        username: [{required: true, message: 'please enter your account', trigger: 'blur'}],
                        password: [{required: true, message: 'enter your password', trigger: 'blur'}]
                    },
                    checked: false
                }
            },
            methods: {
                //这是获得微信二维码的
                getWxLoginUrl(event){
                    this.$http.get('/login').then((res)=>{
                        console.debug(res) ;
                        let { msg, success,wxLoginUrl, resultObj } = res.data;
                        if (!success) {
                            this.$message({
                                message: msg,
                                type: 'error'
                            });
                        } else {
                            //通过这种方式跳转页面
                           // this.$router.push({ path: '/wxLoginUrl' });
                            window.location.href=wxLoginUrl
                        }
                    })
                },
                //普通登陆
                handleSubmit(event){
                    var _this = this;
                    this.$refs.ruleForm2.validate((valid) => {
                        if (valid) {
                            this.logining = true;
                            //把参数设置进去,发送请求判断数据库中是否有参数
                            var loginParams = { username: this.ruleForm2.username, password: this.ruleForm2.password };
                            //equestLogin(loginParams).then(data => {
                            this.$http.post('/login',loginParams).then(data=>{
                                this.logining = false;
                                console.debug(data);
                                //获得返回值
                                let { msg, success, resultObj } = data.data;
                                if (!success) {
                                    this.$message({
                                        message: msg,
                                        type: 'error'
                                    });
                                } else {
                                    //登陆成功,绑定数据到session下次登陆就可以使用了
                                    sessionStorage.setItem('user', JSON.stringify(resultObj.user.username));
                                    sessionStorage.setItem('token',resultObj.token);
                                    //跳到页面
                                    this.$router.push({ path: '/echarts' });
                                }
                            });
                        } else {
                            console.log('error submit!!');
                            return false;
                        }
                    });
                }
            }
        };
    </script>
    
    <style scoped>
      .login-container {
         100%;
        height: 100%;
      }
      .login-page {
        -webkit-border-radius: 5px;
        border-radius: 5px;
        margin: 180px auto;
         350px;
        padding: 35px 35px 15px;
        background: #fff;
        border: 1px solid #eaeaea;
        box-shadow: 0 0 25px #cac6c6;
      }
      label.el-checkbox.rememberme {
        margin: 0px 0px 15px;
        text-align: left;
      }
    </style>
    View Code

    在main.js中需要放行路径

    router.beforeEach((to, from, next) => {
      //NProgress.start();
      if (to.path == '/login') {
        sessionStorage.removeItem('user');
      }
      let user = JSON.parse(sessionStorage.getItem('user'));
      if (!user && to.path != '/login') {
        next({ path: '/login' })
      } else {
        next()
      }
    })

    这里是向LoginController中的发送的登陆请求

    @RequestMapping(value = "/login",method = RequestMethod.POST)
        @ResponseBody
        public AjaxResoult login(@RequestBody Employee employee){
            Subject currentUser = SecurityUtils.getSubject();
                //获得令牌传入参数,判断是否是正确的
            if(!currentUser.isAuthenticated()) {
                try {
                    MyUsernamePasswordToken token = new MyUsernamePasswordToken(employee.getUsername(), employee.getPassword());
                    //使用当前用户经行添加
                    currentUser.login(token);
                    //不能在这里return
                    /* return new JsonResult();*/
                } catch (UnknownAccountException e) {
                    //判断用户名是否错误
                    e.printStackTrace();
                    System.out.println("是请输入正确的用户名");
                    return new AjaxResoult().setMsg("用户名或密码错误" + e.getMessage()).setSuccess(false);
                } catch (IncorrectCredentialsException e) {
                    //判断密码是否错误
                    e.printStackTrace();
                    System.out.println("是请输入正确的密码");
                    return new AjaxResoult().setMsg("用户名或密码错误" + e.getMessage()).setSuccess(false);
                } catch (AuthenticationException e) {
                    //所有的错误
                    e.printStackTrace();
                    System.out.println("未知错误");
                    return new AjaxResoult().setMsg("系统错误" + e.getMessage()).setSuccess(false);
                }
            }
            Employee employee1 = (Employee) currentUser.getPrincipal();
            employee.setPassword(null);
            AjaxResoult ajaxResoult = new AjaxResoult();
            //获得sessionId
            Map<String,Object> result = new HashMap<>();
            //除了返回登录成功与否,还要把登录的用户返回前端
            result.put("user",employee1);
            result.put("token",currentUser.getSession().getId());
            ajaxResoult.setResultObj(result);
            return ajaxResoult;
        }
    View Code

    因为前后台发送的服务请求不一杨所以需要判断是否登陆过

    所以需要使用一个类来判断覆写DefaultWebSessionManager

    package cn.jiedada.crm.web.shiro;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.apache.shiro.web.util.WebUtils;
    import org.springframework.util.StringUtils;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import java.io.Serializable;
    
    /**
     *
     * 传统结构项目中,shiro从cookie中读取sessionId以此来维持会话,
     * 在前后端分离的项目中(也可在移动APP项目使用),我们选择在ajax的请求头中传递sessionId,
     * 因此需要重写shiro获取sessionId的方式。
     * 自定义CrmSessionManager类继承DefaultWebSessionManager类,重写getSessionId方法
     *
     */
    public class CrmSessionManager extends DefaultWebSessionManager {
    
        private static final String AUTHORIZATION = "X-Token";
    
        private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
    
        public CrmSessionManager() {
            super();
        }
    
        @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        //取到jessionid
            String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
            HttpServletRequest request1 = (HttpServletRequest) request;
            //如果请求头中有 X-TOKEN 则其值为sessionId
            if (!StringUtils.isEmpty(id)) {
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
                return id;
            } else {
                //否则按默认规则从cookie取sessionId
                return super.getSessionId(request, response);
            }
        }
    
    }
    View Code

     在application中添加

       <!--session管理器通过继承DefaultWebSecurityManager来自定义我们的session-->
        <bean id="crmSessionManager" class="cn.jiedada.crm.web.shiro.CrmSessionManager"></bean>
        <!--shiro的核心对象-->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <!--配置realm-->
            <property name="sessionManager" ref="crmSessionManager"/>
            <property name="realm" ref="myRealm"/>
        </bean>

    密码匹配器

    MD5Util

    package cn.jiedada.crm.web.shiro;
    
    import org.apache.shiro.crypto.hash.SimpleHash;
    
    public class MD5Util {
    
        public static final String SALT = "jiedada";
    
        /**
         * 加密
         * @param source
         * @return
         */
        public static String encrypt(String source){
            SimpleHash simpleHash = new SimpleHash("MD5",source,SALT,10);
            return simpleHash.toString();
        }
    
        public static void main(String[] args) {
    
            System.out.println(encrypt("123456"));
        }
    
    }
    View Code
    package cn.jiedada.crm.web.shiro;
    
    import cn.jiedada.crm.domain.Permission;
    import cn.jiedada.crm.service.IPermissionService;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import java.util.LinkedHashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * 用于返回下面的这些值(这里的值是有顺序的:LinkedHashMap)
     *   <value>
             /login = anon
             /s/permission.jsp = perms[user:index]
             /** = authc
        </value>
     */
    public class ShiroFilterMapFactory {
        @Autowired
        private IPermissionService permissionService;
        public Map<String,String> createMap(){
            Map<String,String> map = new LinkedHashMap<>();
            //anon:需要放行的路径
            map.put("/login","anon");
            map.put("/login","anon");
            map.put("*.js","anon");
            map.put("*.css","anon");
            map.put("/css/**","anon");
            map.put("/js/**","anon");
            map.put("/images/**","anon");
            //perms:权限拦截
            List<Permission> permissions = permissionService.findAll();
            permissions.forEach(p->{
                map.put(p.getUrl(),"aisellPers["+p.getSn()+"]");
            });
            //authc:拦截
            map.put("/**","myFilter");
            return map;
        }
    }
    View Code

    其中的关键是通过用户查询权限的方法在mapper中我写了一个方法

     <!--通过员工找到权限-->
        <select id="findPermissionByEmployee" parameterType="long" resultType="permission">
            SELECT DISTINCT p.*
                from t_employee e
                JOIN t_department d
                ON e.department_id=d.id
                JOIN t_department_role dr
                on dr.department_id=d.id
                JOIN t_role_permission rp
                on dr.role_id=rp.role_id
                JOIN t_permission p
                on p.id=rp.permission_id
                where e.id=#{id}
        </select>

    通过员工找到部门,再找到多对多的角色id,再通过角色id找到多对多找到权限id,找到权限

    因为elementui发送axious请求的时候会先发送一个options的来看是否呢个够通过所以需要写一个过滤器来放行请求

    FormAuthenticationFilter

    package cn.jiedada.crm.web.shiro;
    
    import cn.jiedada.crm.web.wechart.LoginType;
    import cn.jiedada.crm.web.wechart.MyUsernamePasswordToken;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * 自定义身份认证过滤器
     */
    public class MyAuthenticationFilter extends FormAuthenticationFilter {
    
        @Override
        protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
            //如果是OPTIONS请求,直接放行
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            String method = httpServletRequest.getMethod();
            //判断是否是OPTIONS请求
            if("OPTIONS".equalsIgnoreCase(method)){
                return true;
            }
            return super.isAccessAllowed(request, response, mappedValue);
        }
        //薪增方法
        @Override
        protected AuthenticationToken createToken(String username, String password, ServletRequest request, ServletResponse response) {
            boolean rememberMe = isRememberMe(request);
            String host = getHost(request);
            String loginType = LoginType.PASSWORD;//需要密码
    
            if(request.getParameter("loginType")!=null && !"".equals(request.getParameter("loginType").trim())){
                loginType = request.getParameter("loginType");
            }
    
            return new MyUsernamePasswordToken(username, password,loginType,rememberMe,host);
        }
    }
    View Code

     application-shiro中的所以

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
    
        <!--session管理器通过继承DefaultWebSecurityManager来自定义我们的session-->
        <bean id="crmSessionManager" class="cn.jiedada.crm.web.shiro.CrmSessionManager"></bean>
        <!--shiro的核心对象-->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <!--配置realm-->
            <property name="sessionManager" ref="crmSessionManager"/>
            <property name="realm" ref="myRealm"/>
        </bean>
    
    
        <!--Realms-->
        <bean id="myRealm" class="cn.jiedada.crm.web.shiro.MyRealm">
            <property name="credentialsMatcher">
                <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                    <property name="hashAlgorithmName" value="MD5"/>
                    <property name="hashIterations" value="10"/>
                </bean>
            </property>
        </bean>
        <!--自定义过滤器-->
        <bean id="myAuthenticationFilter" class="cn.jiedada.crm.web.shiro.MyAuthenticationFilter"></bean>
        <bean id="aisellPermissionsAuthorizationFilter" class="cn.jiedada.crm.web.shiro.AisellPermissionsAuthorizationFilter"></bean>
        <!--shiro的过滤器配置-->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <property name="securityManager" ref="securityManager"/>
            <property name="loginUrl" value="/login"/>
            <property name="successUrl" value="/s/index"/>
            <property name="unauthorizedUrl" value="/s/unauthorized"/>
            <!--通过key在下面找到我们需要的东西,需要使用value-ref关联-->
            <property name="filters">
                <map>
                    <entry key="myFilter" value-ref="myAuthenticationFilter"></entry>
                    <entry key="aisellPers" value-ref="aisellPermissionsAuthorizationFilter"></entry>
                </map>
            </property>
            <!--在这下面使用我们的myFilter-->
            <property name="filterChainDefinitions">
                <value>
                    /* = anon
                    /js/** = anon
                    /** = myFilter
                </value>
            </property>
        </bean>
    
    
    
    </beans>
  • 相关阅读:
    大二下每周总结
    大二下学期阅读笔记(人月神话)
    大二下学期第一次结对作业(第二阶段)
    大二下学期第一次结对作业(第二阶段)
    大二下学期项目练习(滑屏数据展示)
    elasticSearch中使用聚合查询后只显示10个bucket的问题
    elasticsearch Terms Query 实现类似于sql in查询
    crontab定时调用shell脚本
    java使用static静态变量
    ElasticSearch 复合数据类型——数组,对象和嵌套
  • 原文地址:https://www.cnblogs.com/xiaoruirui/p/11878128.html
Copyright © 2020-2023  润新知