在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="width:100%;">
        <el-button type="primary" style="width: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 {
    width: 100%;
    height: 100%;
  }
  .login-page {
    -webkit-border-radius: 5px;
    border-radius: 5px;
    margin: 180px auto;
    width: 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>

猜你喜欢

转载自www.cnblogs.com/xiaoruirui/p/11878128.html