本篇文章禽最,使用了Vue.js和axios.js做異步請求,我們知道異步請求是不支持跨域的袱饭,為了解決這個問題已經(jīng)有了很多實現(xiàn)方案川无,本例子使用Cros方案,cros是W3C出的標志跨域請求協(xié)議虑乖,文章文字描述比較少懦趋,都是直接貼代碼,大家可以直接復制。
原理:模擬使用Cookie保存sessionId的功能坎缭。
1边臼,登錄時由tomcat服務器將sessionId放到請求頭,發(fā)送給客戶端鸣戴;
2入偷,客戶端將請求頭的sessionId保存到瀏覽器的sessionStorage中;
3,客戶端登錄后的每次請求都以同樣的請求頭將sessionId帶回給服務器亏镰。
最后有案例演示的圖片纸兔,使用的是sublime text3運行l(wèi)ogin.html文件备禀,用來模擬兩個服務器之間發(fā)請求另患,由sublime服務器來請求tomcat服務器租冠,來看是否session一致的纤泵。
需要有Redis和Mysql數(shù)據(jù)庫公荧。
hosts文件修改如下:
127.0.0.1 www.liyucheng.com
127.0.0.1 test1.liyucheng.com
127.0.0.1 test2.liyucheng.com
127.0.0.1 test3.liyucheng.com
127.0.0.1 test4.liyucheng.com
127.0.0.1 test5.liyucheng.com
127.0.0.1 test6.liyucheng.com
127.0.0.1 test7.liyucheng.com
127.0.0.1 test8.liyucheng.com
127.0.0.1 test9.liyucheng.com
1桥爽,目錄結(jié)構(gòu)如下:
2,pom.xml中jar包依賴如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springsession</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springsession</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3赊抖,application.properties文件
spring.session.store-type=redis
server.servlet.session.timeout=1800
#spring.session.hazelcast.flush-mode=on-save
spring.session.redis.namespace=spring.session
spring.redis.host=192.168.1.105
spring.redis.password=
spring.redis.port=6379
spring.datasource.url=jdbc:mysql://192.168.1.105:3306/test
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
mybatis.mapper-locations=classpath:mapper/*.xml
#debug=true
server.port=8080
#server.servlet.context-path=/spring_session
4,Session的配置類如下:
package com.example.springsession.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.web.http.CookieHttpSessionIdResolver;
import org.springframework.session.web.http.DefaultCookieSerializer;
import org.springframework.session.web.http.HeaderHttpSessionIdResolver;
/**
* spring session 配置類
*/
@Configuration
public class MySpringSessionConfig {
// @Bean
public DefaultCookieSerializer setDefaultCookieSerializer() {
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setDomainName("liyucheng.com");
return cookieSerializer;
}
/**
* 使用請求頭作為會話認證
* @return
*/
@Bean
public HeaderHttpSessionIdResolver setHeaderHttpSessionIdResolver() {
return HeaderHttpSessionIdResolver.authenticationInfo();
}
// @Bean
public CookieHttpSessionIdResolver setCookieHttpSessionIdResolver() {
return new CookieHttpSessionIdResolver();
}
}
5,controller的內(nèi)容如下:
package com.example.springsession.controller;
import com.example.springsession.pojo.Account;
import com.example.springsession.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
import java.util.List;
@Controller
@CrossOrigin(allowCredentials = "true", allowedHeaders = {"Authentication-Info", "content-type"}, exposedHeaders = {"Authentication-Info"})
@RequestMapping("/account")
public class AccountController {
@Autowired
private AccountService accountService;
@ResponseBody
@RequestMapping("/add")
public String insert(Account account) {
account = accountService.add(account);
return "success " + account;
}
@ResponseBody
@RequestMapping("/login")
public Boolean selectByPrimary(@RequestBody Account account, HttpSession session) {
boolean success = accountService.login(account);
if(success) {
session.setAttribute("account", account);
}
System.out.println(session.getId());
return success;
}
@ResponseBody
@RequestMapping("/search")
public List<Account> search(HttpSession session) {
System.out.println(session.getId());
if(session.getAttribute("account") == null) {
return null;
}
List<Account> accountList = accountService.search();
return accountList;
}
}
6供填,service內(nèi)容如下:
6.1粘捎,接口
package com.example.springsession.service;
import com.example.springsession.pojo.Account;
import java.util.List;
public interface AccountService {
Account add(Account account);
boolean login(Account account);
List<Account> search();
}
6.2實現(xiàn)類
package com.example.springsession.service.impl;
import com.example.springsession.mapper.AccountMapper;
import com.example.springsession.pojo.Account;
import com.example.springsession.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Override
public Account add(Account account) {
accountMapper.insert(account);
return account;
}
@Override
public boolean login(Account loginAccount) {
Account account = accountMapper.selectByName(loginAccount.getName());
if(account.getPasswd().equals(loginAccount.getPasswd())) {
account.setPasswd("");
loginAccount = account;
return true;
}
return false;
}
@Override
public List<Account> search() {
List<Account> accountList = accountMapper.select();
return accountList;
}
}
7梧奢,mapper內(nèi)容如下:
7.1器虾,mapper接口
package com.example.springsession.mapper;
import com.example.springsession.pojo.Account;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface AccountMapper {
int insert(Account account);
Account selectByPrimary(int id);
Account selectByName(String name);
List<Account> select();
}
7.2,mapper配置文件
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.springsession.mapper.AccountMapper">
<insert id="insert" parameterType="com.example.springsession.mapper.AccountMapper" useGeneratedKeys="true">
insert into account (id, name, passwd) value (#{id }, #{name }, #{passwd })
</insert>
<select id="selectByPrimary" parameterType="int" resultType="com.example.springsession.pojo.Account">
select id, name, passwd from account where id = #{id }
</select>
<select id="selectByName" parameterType="string" resultType="com.example.springsession.pojo.Account">
select id, name, passwd from account where name = #{name }
</select>
<select id="select" resultType="com.example.springsession.pojo.Account">
select * from account
</select>
</mapper>
8,pojo內(nèi)容如下:
package com.example.springsession.pojo;
import java.io.Serializable;
public class Account implements Serializable {
private Integer id;
private String name;
private String passwd;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
}
9,數(shù)據(jù)庫表結(jié)構(gòu)如下:
create table account (
`id` int primary key auto_increment,
`name` varchar(255) not null unique,
`passwd` varchar(255) not null
);
10熊昌,測試用的Html如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陸頁面</title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="name" placeholder="賬戶"><br>
<input type="password" v-model="passwd" placeholder="密碼"><br>
<button v-on:click.prevent="submit">登錄</button>
<button v-on:click="m2">登錄11111</button>
</div>
<script>
var token = '';
axios.default.withCredentials = true;
new Vue({
el: '#app',
data: {
name: 'lisi',
passwd: 'lisi'
},
methods: {
submit: function (event) {
axios.post('/account/login', {
name: this.name,
passwd: this.passwd
}).then(function (response) {
token = response.headers['authentication-info'];
sessionStorage.setItem('authentication-info', token);
axios.defaults.headers.common['authentication-info'] = response.headers["authentication-info"];
console.log(response);
}).catch(function (error) {
console.log(error);
});
},
m2: function (event) {
axios.post('http://test1.liyucheng.com:8080/account/search').then(function (response) {
console.log(response);
});
}
}
})
</script>
</body>
</html>
以上便是這個案例的围详,所有內(nèi)容了雹食。
知識要點是:
1儒鹿,使用@CrossOrigin來支持跨域,配置好用戶可以可以獲取到的請求頭Authentication-Info,以及我們系統(tǒng)支持的請求頭Authentication-Info和content-type鸡岗,并設置接受cookie為true揣苏。
@CrossOrigin(allowCredentials = "true", allowedHeaders = {"Authentication-Info", "content-type"}, exposedHeaders = {"Authentication-Info"})
2个少,在session的配置類中指定使用HeaderHttpSessionIdResolver作為session的認證
/**
* 使用請求頭作為會話認證
* @return
*/
@Bean
public HeaderHttpSessionIdResolver setHeaderHttpSessionIdResolver() {
return HeaderHttpSessionIdResolver.authenticationInfo();
}
3,登錄后我們系統(tǒng)會發(fā)送一個Authentication-Info請求頭,這時需要客戶端自己將其保存起來,然后在每次發(fā)起請求時铃彰,將其帶回給服務器,就可以做到session的效果
sessionStorage.setItem('authentication-info', token);
axios.defaults.headers.common['authentication-info'] = sessionStorage.getItem('authentication-info');
本案例使用Vue的axios發(fā)起異步請求蓝纲,將sessionId保存起來慕嚷,設置axios請求每次都帶上這個信息,起到了支持跨域的session會話。
以下是本次案例的演示效果圖:
1琐脏,登錄按鈕掐暮,跨域請求,得到了sessionId保存起來
2,登錄按鈕11111杜跷,將sessionId帶回給服務器
3驳概,后臺代碼,證明是同一個session
由此可見,這好像實現(xiàn)類了單點登錄功能爪飘,并且支持了跨域,這里選擇請求頭而不用cookie蛙吏,是因為手機App這種是沒有cookie滴泼诱。屉栓。。蜈抓。委可。
謝謝你屈呕,看完了我的文章嗽桩,寫的比較爛罐氨,畢竟是理科生记盒,沒什么文采憎蛤。
祝好~