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