Ssm+vue+ElementUI实现微信第三方登录

ssm+vue+ElementUI实现前后端分离第三方登录功能

这次的分享记录得非常的详细,希望对大家有所帮助!

1.继承ssm需要的jar包和配置文件在我另一篇文章已经写下来了

(第三方登录本地host文件配置:127.0.0.1 bugtracker.itsource.cn)

2.准备shiro模块

2.1 导入shiro需要的jar包(这里web模块也要依赖shiro模块)

 <dependency>
            <groupId>cn.itsource</groupId>
            <artifactId>itsource_service</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-all</artifactId>
            <version>1.4.1</version>
        </dependency>

        <!--servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
            <scope>provided</scope>
        </dependency

2.2 在web.xml配置文件中加入过滤器

<!--shiro-->
 <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>

2.3 准备shiro spring配置文件
(applicationContext-shiro.xml)

Itsource_auth_shiro中的applicationContext-shiro.xml
<?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">

   <!--shiro的核心对象 realm-->
   <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
             <!--配置realm-->
       <property name="realm" ref="authRealm"/>
   </bean>

   <!--Realms-->
   <bean id="authRealm" class="cn.itsource.shiro.realm.AuthenRealm">
       <property name="credentialsMatcher">
           <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
               <property name="hashAlgorithmName" value="MD5"/>
               <property name="hashIterations" value="10"/>
           </bean>
       </property>
   </bean>

   <!--shiro的过滤器配置 web.xml的代理过滤器名称一样-->
   <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
       <property name="securityManager" ref="securityManager"/>
       <property name="loginUrl" value="/s/login"/>
       <property name="successUrl" value="/s/index"/>
       <property name="unauthorizedUrl" value="/s/unauthorized"/>
      
       <property name="filterChainDefinitions">
           <value>
               /login = anon
               /** = authc
           </value>
       </property>
   </bean>

2.4 把shiro配置文件(applicationContext-shiro.xml)集成到Spring(这里注意集成到的是Spring而不是SpringMvc)

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>

      classpath*:applicationContext.xml,
      classpath*:applicationContext-shiro.xml
    </param-value>
  </context-param>
  <!--Spring监听器 ApplicationContext 载入 -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

3.实现登录

3.1. ==密码加密保存工具 (这里用的是MD5加密方式) ==

package cn.fours.util;

import org.apache.shiro.crypto.hash.SimpleHash;

public class MD5Util {

    public static final String SALT = "itsource";

    /**
     * 加密
     *
     * @param source
     * @return
     */
    public static String encrypt(String source) {
        SimpleHash simpleHash = new SimpleHash("MD5", source, SALT, 10);
        return simpleHash.toString();
    }

}

3.2 在添加调用添加员工时添加加密方式密码

employee.setPassword(MD5Util.encrypt(employee.getPassword()));

3.3 前端vue登录实现(这里的页面是最终的页面,包括了最后第三方登录的,注意的是我这里的图片是用的绝对路径,引用时一定要注意删掉不然会报错)

<template>
  <div class="note" :style="note">
    <img src="404.vue"/>
  <el-form  :model="ruleForm2" :rules="rules2" ref="ruleForm2" label-position="left" label-width="0px" class="demo-ruleForm login-container">
    <h3 class="title" style="color: springgreen">系统登录</h3>
    <el-form-item prop="username">
      <el-input type="text" 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" checked class="remember">记住密码</el-checkbox>
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
    <el-text  >没有账号?<router-link :to="`/register`" style="color: red;font-size: 15px" >注册</router-link></el-text>

    <el-form-item style="width:100%;">
      <el-button type="primary" style="width:100%;" @click.native.prevent="handleSubmit2" :loading="logining">登录</el-button>
    </el-form-item>
   <h3 style="text-align:center;color: deepskyblue;">其它登录方式?</h3>
    <el-form-item>
      <el-button   circle ><img width="55px" style="background-color: white" height="40px" src="../img/qq.jpg"/></el-button>
      <el-button   @click="wxloginSubmit" circle ><img width="55px" style="background-color: white" height="40px" src="../img/微信.jpg"/></el-button>
      <el-button   circle ><img width="55px" style="background-color: white" height="40px" src="../img/微博.jpg"/></el-button>
    </el-form-item>
  </el-form>
    <img src="404.vue"/>
  </div>
</template>
<script>
    export default {
        data() {
            return {
                wxurl:[],
                logining: false,
                ruleForm2: {
                    username: '',
                    password: '',
                },
                rules2: {
                    username: [
                        { required: true, message: '请输入账号', trigger: 'blur' },
                    ],
                    password: [
                        { required: true, message: '请输入密码', trigger: 'blur' },
                    ]
                },
                note:{
                    backgroundImage: "url(" + require("D:\\java\\ideawork\\ssm-front\\src\\img\\224148-15680401081061.jpg") + ")",
                    backgroundRepeat: "no-repeat",
                    backgroundSize: "1520px auto",
                },
                checked: true
            };
        },
        methods: {
            handleReset2() {
                this.$refs.ruleForm2.resetFields();
            },
            //第一种方式
           /* handleSubmit2(ev) {
                var _this = this;
                this.$refs.ruleForm2.validate((valid) => {
                    if (valid) {
                        this.logining = true;
                        var loginParams = {
                            username: this.ruleForm2.username,
                            password: this.ruleForm2.password };
                        this.$http.post("/login",loginParams).then(res => {
                            this.logining = false;
                            if(res.data.code == 200){
                                this.$router.push({ path: '/echarts' });
                            }
                            else {
                                this.$message({
                                    message: msg,
                                    type: 'error'
                                });
                            }
                        });
                    } else {
                        console.log('error submit!!');
                        return false;
                    }
                });
            },*/

            handleSubmit2(ev) {
                var _this = this;
                this.$refs.ruleForm2.validate((valid) => {
                    if (valid) {
                        this.logining = true;
                        var loginParams = { username: this.ruleForm2.username, password: this.ruleForm2.password };
                        this.$http.post("/login",loginParams).then(data => {
                            this.logining = false;
                            let { msg, success, resultObj} = data.data;
                            console.log(data)
                            if (!success) {
                                this.$message({
                                    message: msg,
                                    type: 'error'
                                });
                            } else {
                                console.log("aaaaa")
                                //登录成功跳转/table的路由地址
                                console.log(resultObj)
                                sessionStorage.setItem('user', JSON.stringify(resultObj.user));
                                sessionStorage.setItem('token', resultObj.token); //不要加字符串转换了巨大的坑
                                //修改登录成功后跳转到首页
                                this.$router.push({ path: '/echarts'});
                            }
                        });
                    } else {
                        console.log('error submit!!');
                        return false;
                    }
                });
            },

            //执行调出二维码方法
            wxloginSubmit(ev) {
                var _this = this;
                        this.$http.get("/wxlogin").then(res => {
                            this.logining = false;
                            this.wxurl=res.data;
                            console.log(this.wxurl)
                            window.location.href = this.wxurl;
                           /* this.$router.push({
                               /!* path: this.wxurl*!/
                              /!* path:"https://open.weixin.qq.com/connect/qrconnect?appid=wxd853562a0548a7d0&redirect_uri=http://bugtracker.itsource.cn/callback&response_type=code&scope=snsapi_login&state=STATE#wechat_redirect"*!/
                            /!*  path:"https://open.weixin.qq.com/connect/qrconnect?"+this.wxurl,*!/
                            });*/
                        });
            },
        }
    }
</script>

<style lang="scss" scoped>
  .login-container {
    /*box-shadow: 0 0px 8px 0 rgba(0, 0, 0, 0.06), 0 1px 0px 0 rgba(0, 0, 0, 0.02);*/
    -webkit-border-radius: 5px;
    border-radius: 5px;
    -moz-border-radius: 5px;
    background-clip: padding-box;
    margin: 100px auto;
    width: 350px;
    padding: 35px 35px 15px 35px;
    background: 00ff00ff;
    border: 1px solid #eaeaea;
    box-shadow: 0 0 25px #cac6c6;
    .title {
      margin: 0px auto 40px auto;
      text-align: center;
      color: #505458;
    }
    .remember {
      margin: 0px 0px 35px 0px;
    }
  }
</style>

3.4 LoginController(这里的登录模块包括了普通用户名密码登录+微信第三方登录+注册+绑定+免密登录,重定向的页面下面会给出详细代码)
还有要注意的就是因为cookie的管理机制导致前后端分离项目中,ajax请求是没有携带cookie的。所以后台是无法通过cookie获取ssionid的,从而无法获取到session对象,下面代码也是写了解决方法的(Token)

package cn.fours.web.controller;

import cn.fours.common.domain.Login;
import cn.fours.common.domain.WxUser;
import cn.fours.service.ILoginService;
import cn.fours.service.IWxUserService;
import cn.fours.util.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 微信登录
 */
@Controller
@CrossOrigin
public class WxLoginController {
    @Autowired
    private IWxUserService service;
    @Autowired
    private ILoginService loginService;
    
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    @ResponseBody
    public JsonResult login(@RequestBody Login login){
        Subject subject = SecurityUtils.getSubject();
      
        if(!subject.isAuthenticated()){
            try {
                MyUsernamePasswordToken token = new MyUsernamePasswordToken(login.getUsername(),login.getPassword());
              
                subject.login(token);
            } catch (UnknownAccountException e) {
                e.printStackTrace();
                return new JsonResult("用户名不存在!");
            } catch (IncorrectCredentialsException e){
                e.printStackTrace();
                return new JsonResult("密码错误!");
            } catch (AuthenticationException e){
                e.printStackTrace();
                return new JsonResult("你登个鸡巴!");
            }
        }
        JsonResult jsonResult = new JsonResult();
        Login login1 = (Login) subject.getPrincipal();
        
        login.setPassword(null);
        HashMap map = new HashMap<>();
        map.put("user",login1);
        map.put("token",subject.getSession().getId());
       
        
        jsonResult.setResultObj(map);
        return jsonResult;
    }
    @RequestMapping(value = "/register", method = RequestMethod.POST)
    @ResponseBody
    public JsonResult register(@RequestBody Login login) {
        
        JsonResult result = new JsonResult();
        //密码一致验证
/*		if (!Objects.equals(login.getPassword(), comfirmPassword)) {
			result.setMessage("密码不一致");
			result.setSuccess(false);
			return result;
		}*/
        login.setPassword(MD5Util.encrypt(login.getPassword()));

        String password = login.getPassword();

        String encrypt = MD5Util.encrypt(password);

        login.setPassword(encrypt);
        try {
            loginService.save(login);
            
        } catch (Exception e){
            result.setSuccess(false);
            result.setMsg("注册失败,请重试");
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 拉起二维码
     * 跳转到本地的login
     * @return
     */
    @RequestMapping(value = "/wxlogin",method =RequestMethod.GET )
    @ResponseBody
    public String login(Model model){
        System.out.println("242424");
        String url = WxConstants.CODEURL.replaceAll("APPID", WxConstants.APPID).
                replaceAll("CALLBACK", WxConstants.CALLBACK)
                .replaceAll("SCOPE", WxConstants.SCOPE);
        model.addAttribute("wxLoginUrl",url);
        return url;
    }
    @RequestMapping(value = "/callback",method =RequestMethod.GET )
    public String callBack(String code,String state) throws Exception {
        /**
         * 获取code
         *发送请求获取ak
         */
        String akUrl = WxConstants.ACCESSTOKEURL.replaceAll("APPID",
                WxConstants.APPID).replaceAll("SECRET", WxConstants.APPSECRET)
                .replaceAll("CODE", code);
        /**
         * 发送请求获取ak
         */
        String responseStr = HttpClientUtils.httpGet(akUrl, null);
        
        /**
         * 发送请求,获取用户信息''
         * json字符串转换为对象
         */
        JSONObject jsonobject = (JSONObject)JSON.parse(responseStr);
        String access_token = jsonobject.getString("access_token");
        
        String openid = jsonobject.getString("openid");
        System.out.println("openid:"+jsonobject.getString("openid"));


        /**
         * 拿到ak和openid再发送请求
         * 发送用户信息的请求地址
         */
        String userInfoUrl = WxConstants.USERINFOURL.replaceAll("ACCESS_TOKEN", access_token).replaceAll("OPENID", openid);
        String userInfoStr = HttpClientUtils.httpGet(userInfoUrl, null);
        

        /**
         * 判断用户是否绑定过,没有绑定就跳转到绑定页面(用户名和密码)
         * 根据openid进行查询用户
         */
        JSONObject userInfo = (JSONObject)JSON.parse(userInfoStr);
        String openid1 = userInfo.getString("openid");
        WxUser wxuser = service.findWxUserById(openid);
        if(wxuser==null){
            String nickname = userInfo.getString("nickname");
            System.out.println("nickname:"+nickname);
            String headimgurl = userInfo.getString("headimgurl");
            String unionid = userInfo.getString("unionid");
            wxuser = new WxUser();
            //保存到数据库
            wxuser.setOpenid(openid1);
            wxuser.setNickname(nickname);
            wxuser.setHeadimgurl(headimgurl);
            wxuser.setUnionid(unionid);
            service.save(wxuser);
            //跳转到绑定页面,绑定一个用户
            return "redirect:http://localhost:8080/#/bind?openid="+openid;
        }else {
            //说明有这个人,登录过得,如果没有绑定,进行绑定,如果绑定过就免密登录
            if(wxuser.getEmpid()!=null){
                //查询单个绑定数据
                Login login = loginService.queryOne(wxuser.getEmpid());
                //传name参数,免密只需要name
                MyUsernamePasswordToken token = new MyUsernamePasswordToken(login.getUsername());
                Subject subject = SecurityUtils.getSubject();
                subject.login(token);
                Login mmlogin = (Login) subject.getPrincipal();
                Serializable tokenid = subject.getSession().getId();
                //扫码免密登录成功之后直接跳转到登陆成功页面
                return "redirect:http://localhost:8080/#/mianmi?user="+mmlogin+"tokenid="+tokenid;
            }
        }
        return null;
    }

    //没有登录过就绑定用户(绑定方法)
    @RequestMapping(value = "/binder",method = RequestMethod.POST)
    @ResponseBody
    public JsonResult binder(@RequestBody Map<String,String> map ){
        //拿到用户名,密码,openid
        String username = map.get("username");
        String password = map.get("password");
        String openid = map.get("openid");
        //根据openid去查询
        WxUser wxUser = service.findWxUserById(openid);
        if(wxUser!=null){
            //根据username查询用户id(还没有写这一个方法)
            /*WxLoginController login = loginService.getByUsername(username);*/
            Login byUsername = loginService.getByUsername(username);
            Long id = byUsername.getId();
            wxUser.setEmpid(id);
            //更新,绑定之后
            service.update(wxUser);
            //免密登录---认证和授权
            return new JsonResult("绑定成功");
        }
        return new JsonResult("绑定失败");
    }
}

3.5 退出登录

sessionStorage.removeItem('token');

3.6 Token放在请求体里面,所以在前端main.js中主动携带Token

axios.interceptors.request.use(config => {
   if (sessionStorage.getItem('token')) {
       // 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改
       config.headers['X-Token'] = sessionStorage.getItem('token')
   }
   console.debug('config',config)
   return config
}, error => {
   // Do something with request error
   Promise.reject(error)
})

3.7 applicationContext-shiro.xml

Shirospring配置文件
  <!--session管理器-->
    <bean id="sessionManager" class="cn.itsource.shiro.util.CrmSessionManager"/>

    <!--shiro的核心对象-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="sessionManager" ref="sessionManager"/>
        <!--配置realm-->
        <property name="realm" ref="authRealm"/>
    </bean>

3.8 CrmSessionMannager

package cn.fours.util;

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.HttpServletRequest;
import java.io.Serializable;

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)) {
            System.out.println(id+"jjjjjjjjj"+request1.getRequestURI()+request1.getMethod());
            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);
        }
    }
}

3.9 这里前后端分离是会出现跨域的,所以在预检请求放行OPTIONS (cors跨域处理时,每次都要跨域预检查,也就是发一个options请求,只要是这样的请求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">

<bean id="myAuthc" class="cn.itsource.shiro.util.MyAuthenticationFilter"/>

    <!--shiro的过滤器配置-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/s/login"/>
        <property name="successUrl" value="/s/index"/>
        <property name="unauthorizedUrl" value="/s/unauthorized"/>
        <property name="filters">
            <map>
                <entry key="myAuthc" value-ref="myAuthc"/>
            </map>
        </property>
        <property name="filterChainDefinitions">
            <value>
                /login = anon
                /** = myAuthc
            </value>
        </property>
    </bean>

3.10 自定义身份认证过滤器

package cn.fours.util;

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;

/**
 * 登录认证Token的处理
 */
public class MyAuthenticationFilter extends FormAuthenticationFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        //如果是OPTIONS请求,直接放行
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String method = httpServletRequest.getMethod();
        System.out.println(method);
        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);
    }
}

4 微信登录

4.1 工具类的封装

package cn.fours.util;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class HttpClientUtils {

   /**
    * http请求工具类,post请求
    *
    * @param url    url
    * @param params json字符串的参数
    * @return
    * @throws Exception
    */
   public static String httpPost(String url, String params) throws Exception {
   	// 创建httpClient对象
   	DefaultHttpClient defaultHttpClient = null;
   	try {
   		defaultHttpClient = new DefaultHttpClient();
   		HttpPost httpPost = new HttpPost(url);
   		httpPost.setHeader("Content-Type", "application/json;charset=ut-8");
   		if (params != null) {
   			System.out.println("请求参数:" + params);
   			// 设置请求参数
   			HttpEntity httpEntity = new StringEntity(params, "utf-8");
   			httpPost.setEntity(httpEntity);
   		}
   		// 执行post请求,并得到相应结果
   		HttpResponse httpResponse = defaultHttpClient.execute(httpPost);
   		if (httpResponse.getStatusLine().getStatusCode() != 200) {
   			String errorLog = "请求失败,errorCode:" + httpResponse.getStatusLine().getStatusCode();
   			throw new Exception(url + errorLog);
   		}
   		// 解析结果
   		HttpEntity responseEntity = httpResponse.getEntity();
   		String responseStr = EntityUtils.toString(responseEntity, "utf-8");
   		System.out.println("请求结果:" + responseStr);
   		return responseStr;
   	} catch (ClientProtocolException e) {
   		e.printStackTrace();
   		throw e;
   	} catch (IOException e) {
   		e.printStackTrace();
   		throw e;
   	} finally {
   		if (defaultHttpClient != null)
   			defaultHttpClient.getConnectionManager().shutdown();
   	}
   }

   /**
    * http请求工具类,get请求
    *
    * @param url    请求地址:可以已经带参数(?),也可以没有带参数,在params中传过来
    * @param params 参数:值支持字符串和list
    * @return
    * @throws Exception
    */
   public static String httpGet(String url, Map<String, Object> params) throws Exception {
   	DefaultHttpClient defaultHttpClient = null;
   	try {
   		defaultHttpClient = new DefaultHttpClient();
   		if (params != null) {
   			// 参数的拼接
   			StringBuilder stringBuilder = new StringBuilder();
   			Iterator<String> iterator = params.keySet().iterator();
   			String key;
   			while (iterator.hasNext()) {
   				key = iterator.next();
   				Object val = params.get(key);
   				if (val instanceof List) {
   					// 如果是list,则遍历拼接
   					List v = (List) val;
   					for (Object o : v) {
   						stringBuilder.append(key).append("=").append(o.toString()).append("&");
   					}
   				} else {
   					// 字符串:直接拼接
   					stringBuilder.append(key).append("=").append(val.toString()).append("&");
   				}
   			}
   			// 删除最后一个&
   			stringBuilder.deleteCharAt(stringBuilder.length() - 1);
   			if (url.indexOf("?") > 0) {
   				// url地址本身包含?
   				url = url + "&" + stringBuilder.toString();
   			} else {
   				url = url + "?" + stringBuilder.toString();
   			}
   		}
   		System.out.println("请求地址:" + url);
   		HttpGet httpGet = new HttpGet(url);
   		httpGet.setHeader("Content-Type", "application/json;charset=ut-8");
   		// 执行
   		HttpResponse httpResponse = defaultHttpClient.execute(httpGet);
   		if (httpResponse.getStatusLine().getStatusCode() != 200) {
   			String errorLog = "请求失败,errorCode:" + httpResponse.getStatusLine().getStatusCode();
   			throw new Exception(url + errorLog);
   		}
   		// 解析结果
   		HttpEntity responseEntity = httpResponse.getEntity();
   		String responseStr = EntityUtils.toString(responseEntity, "utf-8");
   		System.out.println("请求结果:" + responseStr);
   		return responseStr;
   	} catch (ClientProtocolException e) {
   		e.printStackTrace();
   		throw e;
   	} catch (IOException e) {
   		e.printStackTrace();
   		throw e;
   	} finally {
   		if (defaultHttpClient != null)
   			defaultHttpClient.getConnectionManager().shutdown();
   	}
   }
}

4.2 常量封装

package cn.fours.util;

public class WxConstants {
   /**
    * 应用程序id
    */
   public  final static String APPID = "wxd853562a0548a7d0";

   //用户授权后微信的回调域名
   public final static String CALLBACK="http://bugtracker.itsource.cn/callback";
   /**
    * pc端常量
    */
   public final static String SCOPE = "snsapi_login";
   /**
    * 应用密码
    */
   public final static String APPSECRET = "4a5d5615f93f24bdba2ba8534642dbb6";
   //微信上获取code的地址---拉起二维码
   public final static String CODEURL = "https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=CALLBACK&response_type=code&scope=SCOPE&state=STATE#wechat_redirect";
   //微信上获取ak的地址
   public final static String ACCESSTOKEURL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
   //微信上获取用户信息的地址
   public final static String USERINFOURL = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";

}

4.2.1 重写登录密码匹配器

package cn.fours.util;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;

/**
 * 
 * 重写密码登录匹配器
 */
public class MyHashedCredentialsMatcher extends HashedCredentialsMatcher {
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        MyUsernamePasswordToken mupt = (MyUsernamePasswordToken) token;
        if (mupt.getLoginType().equals(LoginType.NOPASSWD)) {
            //免密登录
            return true;
        }
        if (mupt.getLoginType().equals(LoginType.PASSWORD)) {
            //密码登录
            return super.doCredentialsMatch(token, info);
        } else {
            return false;
        }
    }
}

4.2.2 配置登录类型常量

package cn.fours.util;

//登录类型常量
public class LoginType {
   public static final String NOPASSWD = "NoPassword";
   public static final String PASSWORD = "Password";
}

4.2.3 在自定义身份认证过滤器(MyAuthenticationFilter )新增方法

//薪增方法
   @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);
   }
}

4.2.4 重写UsernamePasswordToken(MyUsernamePasswordToken )

package cn.fours.util;

import org.apache.shiro.authc.UsernamePasswordToken;

public class MyUsernamePasswordToken extends UsernamePasswordToken {

   private String loginType;

   public MyUsernamePasswordToken() {
       super();
   }
   /**账号密码登录*/
   public MyUsernamePasswordToken(String username, String password, String loginType, boolean rememberMe, String host) {
       super(username, password, rememberMe, host);
       this.loginType = loginType;
   }

   /**免密登录*/
   public MyUsernamePasswordToken(String username) {
       super(username, "", false, null);
       this.loginType = LoginType.NOPASSWD;
   }
  //需要用户名和密码登录
   public MyUsernamePasswordToken(String username, String password) {
       super(username, password, false, null);
       this.loginType = LoginType.PASSWORD;
   }

   public String getLoginType() {
       return loginType;
   }

   public void setLoginType(String loginType) {
       this.loginType = loginType;
   }
}

==4.3 bind前端页面 ==

<template>
    <el-form :model="ruleForm2" :rules="rules2" ref="ruleForm2" label-position="left" label-width="0px" class="demo-ruleForm login-container">
        <h3 class="title">系统登录</h3>
        <el-form-item prop="username">
            <el-input type="text" 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-form-item style="width:100%;">
            <el-button type="primary" style="width:100%;" @click.native.prevent="binder" :loading="logining">绑定</el-button>
        </el-form-item>
        <el-text >没有账号?<router-link :to="`/register`" >注册</router-link></el-text>
    </el-form>
</template>
<script>
    export default {
        data() {
            return {
                logining: false,
                ruleForm2: {
                    username: '',
                    password: '',
                },
                rules2: {
                    username: [
                        { required: true, message: '请输入账号', trigger: 'blur' },
                    ],
                    password: [
                        { required: true, message: '请输入密码', trigger: 'blur' },
                    ]
                },
                checked: true
            };
        },
        methods: {
            handleReset2() {
                this.$refs.ruleForm2.resetFields();
            },
            binder(ev) {
                var _this = this;
                this.$refs.ruleForm2.validate((valid) => {
                    if (valid) {
                        this.logining = true;
                        //拿到后台返回过来的地址数据,后台返回返回方法中是返回了openid的
                        var url =  window.location.href;
                        //根据=来过去地址中的openid,split截取方法,传过来的是一个数组,以=号为标准,取第一个
                        var openid = url.split("=")[1];
                        //把用户名和密码传到后台
                        var loginParams = { username: this.ruleForm2.username, password: this.ruleForm2.password,openid:openid};
                        this.$http.post("/binder",loginParams).then(data => {
                            this.logining = false;
                            let { msg, success} = data.data;
                            console.log(msg)
                            console.log(success)
                            console.log(data)
                            console.log(data.data)
                                this.$message({
                                    message: msg,
                                    type: 'success'
                                });
                                //绑定成功跳转
                                //修改登录成功后跳转到首页
                                this.$router.push({ path: '/login'});
                        });
                    } else {
                        console.log('error submit!!');
                        return false;
                    }
                });
            },
        }
    }
</script>

<style lang="scss" scoped>
    .login-container {
        /*box-shadow: 0 0px 8px 0 rgba(0, 0, 0, 0.06), 0 1px 0px 0 rgba(0, 0, 0, 0.02);*/
        -webkit-border-radius: 5px;
        border-radius: 5px;
        -moz-border-radius: 5px;
        background-clip: padding-box;
        margin: 180px auto;
        width: 350px;
        padding: 35px 35px 15px 35px;
        background: #fff;
        border: 1px solid #eaeaea;
        box-shadow: 0 0 25px #cac6c6;
        .title {
            margin: 0px auto 40px auto;
            text-align: center;
            color: #505458;
        }
        .remember {
            margin: 0px 0px 35px 0px;
        }
    }
</style>

4.4 register前端页面

<template>
 <!--:model="tenant" 数据双向绑定-->
 <!--ref="tenantForm" id="tenantForm",给form取一个名字-->
 <!--:rules="formRules" 校验规则-->
 <el-form :model="login"  ref="tenantForm" :rules="formRules" label-position="left" label-width="100px" class="demo-ruleForm login-container">
   <h3 class="title">员工注册</h3>
   <el-form-item prop="username" label="姓名">
     <el-input type="text" v-model="login.username"  auto-complete="off"  placeholder="请输入姓名!"  ></el-input>
   </el-form-item>
   <!--<el-form-item prop="img_url" label="头像">
     <el-input type="text" v-model="login.img_url" auto-complete="off" placeholder="请选择头像!"></el-input>
   </el-form-item>-->
   <el-form-item prop="img_url" label="头像">
       <el-upload
               class="upload-demo"
               action="http://localhost/file/upload"
               :on-preview="handlePreview"
               :on-remove="handleRemove"
               :on-success="handleSuccess"
               :file-list="fileList"
               list-type="picture">
         <el-button size="small" type="primary">点击上传</el-button>
       </el-upload>
   </el-form-item>
   <el-form-item prop="password" label="密码">
     <el-input type="password"v-model="login.password"  auto-complete="off" placeholder="请输入密码!"></el-input>
   </el-form-item>
   <el-form-item prop="comfirmPassword" label="确认密码">
     <el-input  type="password"  v-model="login.comfirmPassword"  auto-complete="off"  placeholder="请输入确认密码!"></el-input>
   </el-form-item>
   <el-form-item style="width:100%;">
     <el-button type="primary" @click.native.prevent="settledIn">注册</el-button>
     <el-button type="primary" @click="resetForm('form')">重置</el-button>
   </el-form-item>
 </el-form>
</template>
<script>
   export default {
       data() {
           //确认comfirmPassword的密码值
           var validatePass2 = (rule, value, callback) => {
               if (value === "") {
                   callback(new Error("请输入确认密码"));
               } else if (value !== this.login.password) {
                   callback(new Error("两次输入密码不一致!"));
               } else {
                   callback();
               }
           };
           return {
               fileList: [],
               login: {
                   username: "",
                   img_url: "",
                   password: "",
                   comfirmPassword: ""
               },
               tenantForm: {
                   username: "",
                   img_url: "",
                   password: ""
               },
               areaData: [],
               formRules: {
                   username: [
                       { required: true, message: "请输入姓名!", trigger: "blur" }
                   ],
                   img_url: [
                       { required: true, message: "请选择头像!", trigger: "blur" }
                   ],
                   password: [{ required: true, message: "请输入密码!", trigger: "blur" }],
                   comfirmPassword: [
                       { required: true, validator: validatePass2, trigger: "blur" } //自定义校验规则
                   ]
               }
           };
       },
       methods: {
           handleRemove(file, fileList) {
               console.log(file, fileList);
           },
           handlePreview(file) {
               console.log(file);
           },
           handleSuccess(response, file, fileList){
                   this.login.img_url = response;
               },
           settledIn() {
               //注册保存用户
               //验证作用
               this.$refs.tenantForm.validate(valid => {
                   if (valid) {
                       //提交之前的确认代码
                       this.$confirm("确认提交吗?", "提示", {}).then(() => {
                           //拷贝的作用  --把form表单的数据 拷贝到新的对象里面para =  {name:'xxx'}
                           let para = Object.assign({}, this.login);
                           //封装数据
                           let pro=para.province+"";
                           let p=pro.replace(/,/g, "");
                           let tenant = {
                               username: para.username,
                               img_url: para.img_url,
                               password: para.password,
                           };
                           let login = {
                               username: para.username,
                               password: para.password,
                               img_url: para.img_url,
                           };

                           login.tenant = tenant;
                           this.$http.post("/register", login).then(res => {
                               if (res.data.success) {
                                   this.$message({
                                       message: "提交成功",
                                       type: "success"
                                   });
                                   //重置表单
                                   this.$refs["tenantForm"].resetFields();
                                   //跳转登录页面
                                   this.$router.push({ path: "/login" });
                               } else {
                                   //提出失败提示框
                                   this.$message({
                                       message: res.data.message,
                                       type: "error"
                                   });
                               }
                           });
                       });
                   }
               });
           }
       },
       mounted() {}
   };
</script>

<style lang="scss" scoped>
 .login-container {
   -webkit-border-radius: 5px;
   border-radius: 5px;
   -moz-border-radius: 5px;
   background-clip: padding-box;
   margin: 120px auto;
   width: 500px;
   padding: 35px 35px 15px 35px;
   background: #fff;
   border: 1px solid #eaeaea;
   box-shadow: 0 0 25px #cac6c6;
   .title {
     margin: 0px auto 40px auto;
     text-align: center;
     color: #505458;
   }
   .remember {
     margin: 0px 0px 35px 0px;
   }
 }
</style>

4.5 mianmi前端页面(这个页面只是我免密登录时需要携带Token数据和用户名,所以相当于一个跳板,在页面加载之前执行方法跳转到主页),这里不建议这样做,很不安全,地址完全暴露,但是实在是没有想到其它好的办法

<!--免密登录数据传输跳板-->
<template>
</template>
<script>
   export default {
       methods: {
           mianmi(){
               var url =  window.location.href;
               console.log(url)
               //根据=来过去地址中的openid,split截取方法,传过来的是一个数组,以=号为标准,取第一个
               var user = url.split("=")[1];
               console.log(user)
               var token = url.split("tokenid%")[1];
               console.log(token)
               sessionStorage.setItem('user', JSON.stringify(user));
               console.log(user);
               sessionStorage.setItem('token',token); //不要加字符串转换了巨大的坑
               //修改登录成功后跳转到首页
               this.$router.push({ path: '/echarts'});
           }
       },
       mounted() {
          this.mianmi()
       }
   }
</script>

到这里基本的配置和代码详解已经完成了,希望对大家有所帮助!
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值