这是单个项目中的集成: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>
在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; }
因为前后台发送的服务请求不一杨所以需要判断是否登陆过
所以需要使用一个类来判断覆写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); } } }
在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")); } }
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; } }
其中的关键是通过用户查询权限的方法在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); } }
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>