浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到 XSS、CSFR 等攻击。浏览器的正常功能会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。
所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)。
跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。
因为网上提供的跨域解决方式有很多,这里我提供一下我使用的一种方式:
由于我使用axios来发送ajax请求,但是axios中只能使用get和post方法来进行请求数据,没有提供jsonp等方法进行跨域访问数据。所以使用axios直接进行跨域是不可行的,上面我们说过跨域问题的产生原因是浏览器的同源策略的限制,所以客户端通过浏览器访问服务端(B/S)就会产生跨域问题,但是我们配置一个代理的服务器请求另一个服务器中的数据,然后把请求出来的数据返回到我们的代理服务器中,代理服务器再返回数据给我们的客户端,这样就可以实现跨域访问数据。
代码实现
1.配置代理
在config文件夹下的index.js文件中的proxyTable字段
dev: {
// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
port: 8081, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/api': {
target: 'http://localhost:8082', // 你请求的第三方接口
changeOrigin: true,// 在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
pathRewrite: {
// 路径重写,
'^/api': '/'// 替换target中的请求地址
}
}
},
2.设置axios实例
这里为了不用每次发送axios请求的时候写"/api",这里对axios的get,post方法进行了简单的封。
import axios from "axios";
//输出通用axios实例
const instance =axios.create({
timeout:5000,//设置axios请求超时时间
headers:{
"Content-Type":"application/json;charset=utf-8"
}
})
export default {
//封装get post 方法
toPost(url,data){
return instance.post("/api"+url,data);
},
toGet(url,config){
return instance.get("/api"+url,config);
}
}
main.js 中
import MyAxiosInstance from "./myConfig/api";
Vue.prototype.axiosInstance=MyAxiosInstance;//axios 实例
以上就是Vue+axios解决跨域问题!
package com.dzk.web.common.config;
import com.dzk.web.common.security.Handler.MyAuthenticationFailureHandler;
import com.dzk.web.common.security.Handler.MyAuthenticationSuccessHandler;
import com.dzk.web.common.security.filter.LoginFilter;
import com.dzk.web.common.security.service.MyPersistentTokenBasedRememberMeServices;
import com.dzk.web.common.security.service.SystemUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.sql.DataSource;
import java.util.UUID;
/**
* @author dzk
* @version 1.0
* @date 2020/11/17 10:36
*/
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
private final String DEFAULT_REMEMBER_ME_KEY=UUID.randomUUID().toString();
@Value("${myConfig.security.tokenValiditySeconds}")
private int tokenValiditySeconds;
@Autowired
private SystemUserDetailsService systemUserDetailsService;
@Autowired
private DataSource dataSource;//datasource 用的是springboot默认的application.yml中的配置
/**
* 密码加密(strength=5 设置加密强度4-31)
* @return
*/
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder(5);
}
/**
* 过滤,授权
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/test/isLogin").authenticated()//受限资源
.anyRequest().permitAll()
.and()
.rememberMe()//配置rememberMe
.tokenRepository(persistentTokenRepository())
.userDetailsService(systemUserDetailsService)
.tokenValiditySeconds(tokenValiditySeconds) //设置rememberMe失效时间
.rememberMeServices(myPersistentTokenBasedRememberMeServices())
.and()
.cors()
.and()
.csrf().disable()//关闭csrf 功能,登录失败存在的原因
.formLogin().and()
.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);//继承UsernamePasswordAuthenticationFilter,替换原来的UsernamePasswordAuthenticationFilter
}
private RememberMeServices myPersistentTokenBasedRememberMeServices() {
//自定义RememberMeServices
return new MyPersistentTokenBasedRememberMeServices(DEFAULT_REMEMBER_ME_KEY, userDetailsService(), persistentTokenRepository());
}
/**
* 身份验证(BCryptPasswordEncoder加密)
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(systemUserDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
LoginFilter loginFilter() throws Exception{
LoginFilter loginFilter=new LoginFilter();
//设置认证成功返回
loginFilter.setAuthenticationSuccessHandler(new MyAuthenticationSuccessHandler());
//设置认证失败返回
loginFilter.setAuthenticationFailureHandler(new MyAuthenticationFailureHandler());
//这句很关键,重用WebSecurityConfigurerAdapter配置的AuthenticationManager,不然要自己组装AuthenticationManager
loginFilter.setAuthenticationManager(authenticationManagerBean());
loginFilter.setRememberMeServices(myPersistentTokenBasedRememberMeServices());
loginFilter.setFilterProcessesUrl("/login");
return loginFilter;
}
/**
* 持久化token
* Security中,默认是使用PersistentTokenRepository的子类InMemoryTokenRepositoryImpl,将token放在内存中
* 如果使用JdbcTokenRepositoryImpl,会创建表persistent_logins,将token持久化到数据库
*/
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
//自动创建相关的token表(首次运行时需要打开,二次运行时需要注解掉)
//jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
}
package com.dzk.web.common.security.filter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dzk.web.common.exception.LoginMethodException;
import com.dzk.web.common.exception.VerifyCodeException;
import com.dzk.web.common.exception.VerifyCodeTimeOutException;
import com.dzk.web.common.utils.RedisUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author 独照
* @version 1.0
* @date 2020/11/17 13:50
*/
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
Logger logger=LoggerFactory.getLogger(LoginFilter.class);
@Autowired
private RedisUtil redisUtil;//redis工具类
/**
*
* @param request
* @param response
* @return
* @throws AuthenticationException
* @describe 重写attemptAuthentication方法,应为前端使用axios发送ajax请求,通过fastJson 读取request流中的登录信息,
* 后续步骤同其父类类似,因为实现了RememberMe功能,而在后面的AbstractRememberMeServices中是通过request.getParameter(parameter)获取记住我参数,
* 而我们在后端不好对request的parameter进行手动添加参数,因此我们使用request.setAttribute("remember-me",rememberMe);进行传参,后面通过重写AbstractRememberMeServices
* 的rememberMeRequested(HttpServletRequest request, String parameter)方法来接收参数
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if(!request.getMethod().equals("POST")){
//请求不为Post方式
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
if(request.getContentType()==null){
//使用PostMan发送表单登录时会出现空指针异常
throw new LoginMethodException();
}
logger.error("request.getContentType()--->"+request.getContentType());
logger.error("类型比较:"+request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE));
if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) || request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_UTF8_VALUE)) {
//如果是Json数据格式请求登录
String jsonData=getJsonParam(request);
JSONObject jsonObject= JSON.parseObject(jsonData);
String verifyCodeKey = jsonObject.getString("uuid");//获取前端传来的uuid
if (verifyCodeKey == null) {
verifyCodeKey = "";
}
String verifyCodeValue = (String) redisUtil.get(verifyCodeKey);//得到存储在redis中的验证码
String verifyCodeOfUser = jsonObject.getString("captcha");//用户传来的验证码
if (verifyCodeOfUser == null) {
verifyCodeOfUser = "";
}
checkVerifyCode(verifyCodeOfUser, verifyCodeValue);//校验验证是否正确
String username = jsonObject.getString("username");//获取用户名
String password = jsonObject.getString("password");//密码
String rememberMe = jsonObject.getString("rememberMe");//记住我
username = username.trim();
request.setAttribute("remember-me",rememberMe);//设置ememberMe
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}else{
throw new LoginMethodException();//禁止使用非application/json格式的登录方式
}
}
public void checkVerifyCode(String verifyCodeOfUser, String verifyCodeOfRedis){
if(StringUtils.isEmpty(verifyCodeOfRedis)){
throw new VerifyCodeTimeOutException();
}else if(!verifyCodeOfRedis.equalsIgnoreCase(verifyCodeOfUser)){
throw new VerifyCodeException();
}
}
/**
* 获取HttpServletRequest中的Json数据
*
* @param request
* @return
*/
private String getJsonParam(HttpServletRequest request) {
String jsonParam = "";
ServletInputStream inputStream = null;
try {
int contentLength = request.getContentLength();
if (!(contentLength < 0)) {
byte[] buffer = new byte[contentLength];
inputStream = request.getInputStream();
for (int i = 0; i < contentLength; ) {
int len = inputStream.read(buffer, i, contentLength);
if (len == -1) {
break;
}
i += len;
}
jsonParam = new String(buffer, "utf-8");
}
} catch (IOException e) {
logger.error("参数转换成json异常g{}", e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
logger.error("参数转换成json异常s{}", e);
}
}
}
return jsonParam;
}
}
验证失败Handler
package com.dzk.web.common.security.Handler;
import com.dzk.web.common.exception.LoginMethodException;
import com.dzk.web.common.exception.UserNotExistException;
import com.dzk.web.common.exception.VerifyCodeException;
import com.dzk.web.common.exception.VerifyCodeTimeOutException;
import com.dzk.web.common.myEnum.ExceptionCodeEnum;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.*;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* @author 独照
* @version 1.0
* @date 2020/11/18 11:30
*/
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException{
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
Map<String,Object> message=new HashMap<>();
if (exception instanceof LockedException) {
message.put("msg", ExceptionCodeEnum.LOCKEDEXCEPTION.getExceptionCode());//账户被锁定,请联系管理员!
} else if (exception instanceof CredentialsExpiredException) {
message.put("msg",ExceptionCodeEnum.CREDENTIALSEXPIREDEXCEPTION.getExceptionCode());//密码过期,请联系管理员!
} else if (exception instanceof AccountExpiredException) {
message.put("msg",ExceptionCodeEnum.ACCOUNTEXPIREDEXCEPTION.getExceptionCode());//账户过期,请联系管理员!
} else if (exception instanceof DisabledException) {
message.put("msg",ExceptionCodeEnum.DISABLEDEXCEPTION);//账户被禁用,请联系管理员!
}
else if(exception.getCause() instanceof UserNotExistException) {
message.put("msg",ExceptionCodeEnum.USERNOTEXCEPTION.getExceptionCode());//账户不存在!
}
else if (exception instanceof BadCredentialsException) {
message.put("msg",ExceptionCodeEnum.BADCREDENTIALSEXCEPTION.getExceptionCode());//密码输入错误,请重新输入!
}else if(exception instanceof VerifyCodeException){
message.put("msg",ExceptionCodeEnum.VERIFYCODEEXCEPTION.getExceptionCode());//验证码错误!
}
else if(exception instanceof VerifyCodeTimeOutException){
message.put("msg",ExceptionCodeEnum.VERIFYCODETIMEOUTEXCEPTION.getExceptionCode());//验证码过期!
}
else if(exception instanceof LoginMethodException){
message.put("msg",ExceptionCodeEnum.LOGINMETHODEXCEPTION.getExceptionCode());//验证码过期!
}
else{
message.put("msg",ExceptionCodeEnum.EXCEPTION.getExceptionCode());//其他异常exception.getMessage()
}
out.write(new ObjectMapper().writeValueAsString(message));
out.flush();
out.close();
}
}
验证成功Handler
package com.dzk.web.common.security.Handler;
import com.dzk.web.pojo.SystemUserEntity;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.val;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* @author 独照
* @version 1.0
* @date 2020/11/18 11:32
*/
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
SystemUserEntity systemUserEntity = (SystemUserEntity) authentication.getPrincipal();
systemUserEntity.setPassword(null);//密码清空
Map<String,Object> message=new HashMap<>();
HttpSession session=request.getSession();
String sessionId=session.getId();//作为登录标识
systemUserEntity.setSessionId(sessionId);
message.put("msg",1000);//登录成功
message.put("data",systemUserEntity);
String s = new ObjectMapper().writeValueAsString(message);
out.write(s);
out.flush();
out.close();
}
}
验证码过期
package com.dzk.web.common.exception;
import org.springframework.security.core.AuthenticationException;
/**
* @author 独照
* @version 1.0
* @date 2020/11/19 9:51
*/
public class VerifyCodeTimeOutException extends AuthenticationException {
public VerifyCodeTimeOutException() {
super("验证码过期");
}
public VerifyCodeTimeOutException(String msg) {
super(msg);
}
}
验证码错误
package com.dzk.web.common.exception;
import org.springframework.security.core.AuthenticationException;
/**
* @author 独照
* @version 1.0
* @date 2020/11/18 10:04
*/
public class VerifyCodeException extends AuthenticationException {
public VerifyCodeException() {
super("验证码错误");
}
public VerifyCodeException(String message) {
super(message);
}
}
用户名不存
package com.dzk.web.common.exception;
import org.springframework.security.core.AuthenticationException;
/**
* @author 独照
* @version 1.0
* @date 2020/11/17 13:28
*/
/*用户不存在异常*/
public class UserNotExistException extends AuthenticationException {
public UserNotExistException() {
super("该用户名不存在!");
}
public UserNotExistException(String message) {
super(message);
}
}
登录方式异常
package com.dzk.web.common.exception;
import org.springframework.security.core.AuthenticationException;
public class LoginMethodException extends AuthenticationException {
public LoginMethodException(String msg) {
super(msg);
}
public LoginMethodException() {
super("登录方式不正确");
}
}
异常枚举类
package com.dzk.web.common.myEnum;
import com.dzk.web.common.exception.LoginMethodException;
import com.dzk.web.common.exception.UserNotExistException;
import com.dzk.web.common.exception.VerifyCodeException;
import com.dzk.web.common.exception.VerifyCodeTimeOutException;
import org.springframework.security.authentication.*;
/**
* @author 独照
* @version 1.0
* @date 2020/11/23 16:07
*/
public enum ExceptionCodeEnum {
LOCKEDEXCEPTION(new LockedException("账户被锁定"),1102),
ACCOUNTEXPIREDEXCEPTION(new AccountExpiredException("账户过期"),1101),
DISABLEDEXCEPTION(new DisabledException("账户被禁用"),1103),
USERNOTEXCEPTION(new UserNotExistException("账户不存在"),1104),
CREDENTIALSEXPIREDEXCEPTION(new CredentialsExpiredException("密码过期"),1201),
BADCREDENTIALSEXCEPTION(new BadCredentialsException("密码错误"),1202),
VERIFYCODEEXCEPTION(new VerifyCodeException("验证码错误"),1300),
VERIFYCODETIMEOUTEXCEPTION(new VerifyCodeTimeOutException("验证码过期"),1301),
LOGINMETHODEXCEPTION(new LoginMethodException("登入方式错误"),1401),
EXCEPTION(new Exception("未知异常"),1500);
private Exception exceptionClass;
private int exceptionCode;
private ExceptionCodeEnum(Exception exceptionClass, int exceptionCode){
this.exceptionClass=exceptionClass;
this.exceptionCode=exceptionCode;
}
public Exception getExceptionClass() {
return exceptionClass;
}
public int getExceptionCode() {
return exceptionCode;
}
}
记住我原理
需要注意的是登录验证并不难,难点在于启用SpringSecurity的RememberMe功能,因为后面AbstractRememberMeServices类中获取request中的remember-me参数使用的是getParameter方法,因为我们前端是通过ajax传输的登录信息,所以getParameter方法获取不到remember-me参数,我之前想在LoginFilter中向request中添加remember-me参数的方式来使AbstractRememberMeServices中获取到remember-me参数,但是效果并不好,于是我通过request.setAttribute(“remember-me”,rememberMe);方法传输remember-me参数,继承AbstractRememberMeServices的子类PersistentTokenBasedRememberMeServices并重写了rememberMeRequested方法。
package com.dzk.web.common.security.service;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.servlet.http.HttpServletRequest;
public class MyPersistentTokenBasedRememberMeServices extends PersistentTokenBasedRememberMeServices {
public MyPersistentTokenBasedRememberMeServices(String key, UserDetailsService userDetailsService, PersistentTokenRepository tokenRepository) {
super(key, userDetailsService, tokenRepository);
}
@Override
protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
boolean rememberMeFlag= super.rememberMeRequested(request, parameter);
String RememberMeParameter=super.getParameter();
String paramValue =(String) request.getAttribute(RememberMeParameter);
if (paramValue != null) {
if (paramValue.equalsIgnoreCase("true") || paramValue.equalsIgnoreCase("on")
|| paramValue.equalsIgnoreCase("yes") || paramValue.equals("1")) {
return true;
}
}
return rememberMeFlag;
}
}
<template>
<div id="loginBody_div" >
<transition name="el-zoom-in-center">
<div id="form_body" v-show="show" class="transition-box">
<el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="账号" prop="username" label-width="60px">
<el-input type="text" v-model="ruleForm.username" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password" label-width="60px">
<el-input type="password" v-model="ruleForm.password" autocomplete="off"></el-input>
</el-form-item>
<el-row>
<el-col :span="15">
<el-form-item label="验证码" prop="captcha" class="captcha" label-width="60px">
<el-input v-model="ruleForm.captcha" ></el-input>
</el-form-item>
</el-col>
<el-col :span="8" :offset="1">
<div id="div_captcha"><img :src="captchaPath" class="captchaImg" @click="getCaptcha()"></div>
</el-col>
</el-row>
<el-row>
<el-col :span="8" :offset="4">
<el-checkbox v-model="ruleForm.rememberME"><small>Remember Me</small></el-checkbox>
</el-col>
</el-row>
<el-form-item label-width="60px">
<div id="submit_div">
<el-button size="medium" type="primary" @click="submitForm('ruleForm')" round>提交</el-button>
</div>
</el-form-item>
<el-form-item label-width="45px">
<div class="btn_group">
<el-button type="text" size="mini" @click="jumpPage(1)">忘记密码</el-button>
<el-button type="text" size="mini" @click="jumpPage(2)">忘记账号</el-button>
<el-button type="text" size="mini" @click="jumpPage(3)">注册</el-button>
</div>
</el-form-item>
</el-form>
</div>
</transition>
</div>
</template>
<script>
import {
getUUID} from '../../utils';
import GLOBAL from '../../myConfig/globalVariable'
export default {
name: "Login.vue",
data() {
var validateCaptcha =(rule, value, callback) => {
console.log(this.loginStatusCode+" "+this.loginStatusMsg)
if (!value) {
return callback(new Error('验证码不能为空'));
}
if (value.toString().length!=4) {
callback(new Error('验证码长度为4个字符'));
}else if(1300<=parseInt(this.loginStatusCode) && parseInt(this.loginStatusCode)<=1500){
callback(new Error(this.loginStatusMsg));
this.clearLoginStatus();
}
else{
callback();
}
};
//账号前端校验
var validateUsername = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入用户名'));
}
else if(1100<=parseInt(this.loginStatusCode) && parseInt(this.loginStatusCode)<1200){
callback(new Error(this.loginStatusMsg));
this.clearLoginStatus();
}
callback();
};
//密码前端校验
var validatePassword = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入密码'));
}
else if(1200<=parseInt(this.loginStatusCode) && parseInt(this.loginStatusCode)<1300){
callback(new Error(this.loginStatusMsg));
this.clearLoginStatus();
}
callback();
};
return {
ruleForm: {
username: '',
password: '',
captcha: '',
captchaUUID:'',
rememberME:false,
},
rules: {
username: [
{
validator: validateUsername, trigger: 'blur' }
],
password: [
{
validator: validatePassword, trigger: 'blur' }
],
captcha: [
{
validator: validateCaptcha, trigger: 'blur' }
]
},
captchaPath:'/static/testImg/captcha.png',//此处去后端获取
show:false,
loginStatusCode:999,//状态码,判断登录是否成功
loginStatusMsg:""//登录状态信息
};
},
methods: {
submitForm(formName) {
this.loginStatusMsg="";//每次提交时,清空登录状态信息栏
let that=this;
this.$refs[formName].validate(async (valid) => {
if (valid) {
//拿到数据去后台验证
await this.axiosInstance.toPost(GLOBAL.BACKPATH+"/login",{
"captcha":this.ruleForm.captcha,
"uuid":this.ruleForm.captchaUUID,
"username":this.ruleForm.username,
"password":this.ruleForm.password,
"rememberMe":this.ruleForm.rememberME
}).then(async function (response) {
let loginStatusCode=response.data.msg.toString();
//console.log(JSON.stringify(response.data.data.username));
//登录成功后将sessionId 作为登录成功的标识
let userDetails=JSON.stringify(response.data.data);
that.$store.commit("setUserDetails",userDetails);//将userDetails存入vuex 中
that.dealOfLoginStatusCode(loginStatusCode);
}).catch(function (error) {
console.log("服务器异常"+error);
})
} else {
console.log('error submit!!');
return false;
}
});
},
showForm: function () {
this.show = true;
},
dealOfLoginStatusCode(statusCode){
this.loginStatusCode=parseInt(statusCode);//得到状态码
if(this.loginStatusCode==1000){
//登录成功后跳转
this.$router.push("/homePage");
}else{
//失败
this.loginStatusMsg=GLOBAL.STATUSCODE.get(this.loginStatusCode);
this.$refs["ruleForm"].validate(()=>{
});//表单验证
}
},
clearLoginStatus(){
//清除登录状态
this.loginStatusMsg="";
this.loginStatusCode=999;
},
getCaptcha() {
this.ruleForm.captcha="";//请求验证码时清除验证码框中的数值
this.$refs["ruleForm"].clearValidate("captcha",valid => {
});
this.ruleForm.captchaUUID=getUUID();//生成uuid 传输到后台作为redis的key
this.axiosInstance.toGet(GLOBAL.BACKPATH+"/publicResources/getVerifyCodeImg",{
params: {
uuid:this.ruleForm.captchaUUID,
},
responseType: 'arraybuffer'//切记 切记 要写在params 的外面!!!!
}).then((response)=>{
const bufferUrl ='data:image/png;base64,'+ btoa(new Uint8Array(response.data).reduce((data, byte) => data + String.fromCharCode(byte), ''));
return bufferUrl;
}).then(data=>{
this.captchaPath=data;
}).catch(error=>{
console.log("验证码图片请求失败:"+error);
})
},
jumpPage(choice){
if(choice==1){
//forget password
this.$router.push({
path:"/forgetPassword"
})
}else if(choice==2){
//forget Username
this.$router.push({
path:"/forgetUsername"
})
}else if(choice==3){
//register
this.$router.push({
path:"/register"
})
}
}
},
mounted() {
this.getCaptcha();//页面加载时获取验证码
this.showForm();
}
}
</script>
<style>
.captchaImg{
width: 100%;
height: 100%;
background-size: cover;
}
#form_body{
height: 400px;
width: 400px;
margin: 160px auto;
}
#div_captcha{
width: 100%;
height: 40px;
float: right;
}
.demo-ruleForm{
font-weight: 500;
}
#loginBody_div #form_body .el-form-item__label{
color: rgba(30, 36, 35, 0.99) !important;
}
.btn_group{
margin: 0 auto;
margin-top: -25px;
text-align: center;
}
#submit_div button{
margin: 10px auto;
width: 330px;
height: 40px;
}
#loginBody_div{
height:100%;
width: 100%;
position: fixed;
background-size: cover;
background-image: url("/static/Img/BackgroundImg/loginBackgroundImg.jpg");
}
</style>
import Vuex from "vuex";
Vue.use(Vuex);
import Vue from 'vue';
export default new Vuex.Store({
state:{
userDetails:null//初始化token
},
mutations: {
//存储token方法
//设置token等于外部传递进来的值
setUserDetails(state, userDetails) {
state.userDetails = userDetails;
sessionStorage.setItem("userDetails",userDetails)//同步存储token至sessionstorage
/*vuex存储在内存,localstorage(本地存储)则以文件的方式存储在本地,永久保存;sessionstorage( 会话存储 ) ,临时保存。localStorage和sessionStorage只能存储字符串类型*/
// 页面刷新失效
}
},
getters : {
//获取token方法
//判断是否有token,如果没有重新赋值,返回给state的token
getUserDetails:(state)=>{
if (!state.userDetails) {
state.userDetails = sessionStorage.getItem("userDetails");//若vuex中无token就去sessionStorage中查找
}
return state.userDetails;
}
},
})
在每次进入首页时先访问后端,查询是否登录,如果登录将导航至首页并将登录信息存储到sessionStorage中,反之进入Login页面。
import Vue from "vue";
import VueRouter from "vue-router";
import Login from "../views/commons/Login"
import Register from "../views/commons/Register";
import ForgetPassword from "../views/commons/ForgetPassword";
import ForgetUsername from "../views/commons/ForgetUsername";
import HomePage from "../views/Users/HomePage";
import routerFunction from "./routerFunction";
import myVuex from "../myConfig/MyVuex"
import MyAxiosInstance from "../myConfig/api";
Vue.use(VueRouter);
this.axiosInstance=MyAxiosInstance;//axios 实例
const AllRouters=[
{
path:"/login",
name:"login",
component:Login
},{
path:"/register",
name:"register",
component:Register
},{
path:"/forgetPassword",
name:"forgetPassword",
component:ForgetPassword
},{
path:"/forgetUsername",
name:"forgetUsername",
component:ForgetUsername
},
{
path:"/homePage",
name:"homePage",
component:HomePage
}
];
const router=new VueRouter({
mode: 'history',//去掉url的#
routes:AllRouters,
});
router.beforeEach((to,from,next)=>{
//路由守卫
routerFunction.isLogin(to,from,next,myVuex,this);
})
export default router;//导出
import GLOBAL from "../myConfig/globalVariable";
async function isLogin(to,form,next,myVuex,that) {
await isLoginBackstage(that,myVuex);
console.log(" !!!"+JSON.stringify(myVuex.getters.getUserDetails))
const jsonData=JSON.parse(myVuex.getters.getUserDetails);
let sessionId=null;
if(jsonData!=null){
sessionId=jsonData.sessionId.toString();
}
//获取sessionId
if(sessionId){
//如果已登录
//判断是否是进入登录页面
if(to.name=="login"){
next("homePage");
}else{
next(true);
}
}else{
next(true)
}
}
async function isLoginBackstage(that,myVuex) {
await that.axiosInstance.toPost(GLOBAL.BACKPATH+"/user/isLogin",{
}).then(
function (response) {
if(response.data!=""){
console.log(JSON.stringify(response.data.username));
//登录成功后将sessionId 作为登录成功的标识
let userDetails=JSON.stringify(response.data);
myVuex.commit("setUserDetails",userDetails);//将userDetails存入vuex 中
console.log(" ++"+JSON.parse(myVuex.getters.getUserDetails).sessionId);
}
}).catch(function (error) {
console.log("服务器异常"+error);
}
)
}
export default {
isLogin
}
以上内容是我在课程设计中涉及到的问题,特别是在RememberMe功能实现,花费了许多时间,好在最终解决了,也许这并非最好的解决方式,但也为遇到此类问题的朋友提供了一种思路。
因为我还是一名小菜鸟,所以上面有些部分可能不正确,欢迎指正,谢谢!