本篇文章,使用了Vue.js和axios.js做异步请求,我们知道异步请求是不支持跨域的,为了解决这个问题已经有了很多实现方案,本例子使用Cros方案,cros是W3C出的标志跨域请求协议,文章文字描述比较少,都是直接贴代码,大家可以直接复制。
原理:模拟使用Cookie保存sessionId的功能。
1,登录时由tomcat服务器将sessionId放到请求头,发送给客户端;
2,客户端将请求头的sessionId保存到浏览器的sessionStorage中;
3,客户端登录后的每次请求都以同样的请求头将sessionId带回给服务器。
最后有案例演示的图片,使用的是sublime text3运行login.html文件,用来模拟两个服务器之间发请求,由sublime服务器来请求tomcat服务器,来看是否session一致的。
需要有Redis和Mysql数据库。
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,目录结构如下:
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的内容如下:
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内容如下:
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实现类
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内容如下:
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内容如下:
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,数据库表结构如下:
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>
以上便是这个案例的,所有内容了。
知识要点是:
1,使用@CrossOrigin来支持跨域,配置好用户可以可以获取到的请求头Authentication-Info,以及我们系统支持的请求头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,登录后我们系统会发送一个Authentication-Info请求头,这时需要客户端自己将其保存起来,然后在每次发起请求时,将其带回给服务器,就可以做到session的效果
sessionStorage.setItem('authentication-info', token);
axios.defaults.headers.common['authentication-info'] = sessionStorage.getItem('authentication-info');
本案例使用Vue的axios发起异步请求,将sessionId保存起来,设置axios请求每次都带上这个信息,起到了支持跨域的session会话。
以下是本次案例的演示效果图:
1,登录按钮,跨域请求,得到了sessionId保存起来
2,登录按钮11111,将sessionId带回给服务器
3,后台代码,证明是同一个session
由此可见,这好像实现类了单点登录功能,并且支持了跨域,这里选择请求头而不用cookie,是因为手机App这种是没有cookie滴。。。。。
谢谢你,看完了我的文章,写的比较烂,毕竟是理科生,没什么文采。
祝好~