废话不多说,直接上代码。示例如下:

1.   新建Maven项目  session

2.   pom.xml

<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>
<groupId>com.java</groupId>
<artifactId>session</artifactId>
<version>1.0.0</version> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent> <dependencies> <!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.0.0.RELEASE</version>
</dependency> <!-- 热部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.8.RELEASE</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>provided</scope>
</dependency> </dependencies> <build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin> <plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

3.   ResponseUtils.java

package javax.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder; import javax.servlet.http.HttpServletResponse; import com.fasterxml.jackson.databind.ObjectMapper; /**
* HTTP 输出响应内容工具类
*
* @author Logan
* @createDate 2019-02-13
* @version 1.0.0
*
*/
public class ResponseUtils { /**
* 发送HTTP响应信息
*
* @param response HTTP响应对象
* @param message 信息内容
* @throws IOException 抛出异常,由调用者捕获处理
*/
public static void write(HttpServletResponse response, String message) throws IOException {
response.setContentType("text/html;charset=UTF-8"); try (
PrintWriter writer = response.getWriter();
) {
writer.write(message);
writer.flush();
}
} /**
* 发送HTTP响应信息,JSON格式
*
* @param response HTTP响应对象
* @param message 输出对象
* @throws IOException 抛出异常,由调用者捕获处理
*/
public static void write(HttpServletResponse response, Object message) throws IOException {
response.setContentType("application/json;charset=UTF-8");
ObjectMapper mapper = new ObjectMapper(); try (
PrintWriter writer = response.getWriter();
) {
writer.write(mapper.writeValueAsString(message));
writer.flush();
}
} /**
* 下载文件
*
* @param response HTTP响应对象
* @param message 输出对象
* @throws IOException 抛出异常,由调用者捕获处理
*/
public static void write(HttpServletResponse response, File file) throws IOException {
String fileName = file.getName();
try (
OutputStream out = response.getOutputStream();
FileInputStream in = new FileInputStream(file);
) { // 对文件名进行URL转义,防止中文乱码
fileName = URLEncoder.encode(fileName, "UTF-8"); // 空格用URLEncoder.encode转义后会变成"+",所以要替换成"%20",浏览器会解码回空格
fileName = fileName.replace("+", "%20"); // "+"用URLEncoder.encode转义后会变成"%2B",所以要替换成"+",浏览器不对"+"进行解码
fileName = fileName.replace("%2B", "+");
response.setContentType("application/x-msdownload;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName); byte[] bytes = new byte[4096];
int len = -1;
while ((len = in.read(bytes)) != -1) {
out.write(bytes, 0, len);
}
out.flush();
}
} }

4.   SessionStarter.java

package com.java;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; /**
* <blockquote><pre>
*
* 主启动类
*
* </pre></blockquote>
*
* @author Logan
*
*/
@SpringBootApplication
public class SessionStarter { public static void main(String[] args) {
SpringApplication.run(SessionStarter.class, args);
} }

5.   SessionInformationExpiredStrategyImpl.java

package com.java.session;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.utils.ResponseUtils; import org.springframework.security.web.session.SessionInformationExpiredEvent;
import org.springframework.security.web.session.SessionInformationExpiredStrategy; /**
* Session过期处理策略
*
* @author Logan
* @createDate 2019-02-13
* @version 1.0.0
*
*/
public class SessionInformationExpiredStrategyImpl implements SessionInformationExpiredStrategy { @Override
public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException { ResponseUtils.write(event.getResponse(), "你的账号在另一地点被登录");
} }

6.   ApplicationContextConfig.java

package com.java.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; /**
* 配置文件类
*
* @author Logan
* @createDate 2019-02-13
* @version 1.0.0
*
*/
@Configuration
public class ApplicationContextConfig { /**
* 配置密码编码器,Spring Security 5.X必须配置,否则登录时报空指针异常
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
} }

7.   LoginConfig.java

package com.java.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import com.java.session.SessionInformationExpiredStrategyImpl; /**
* 登录相关配置
*
* @author Logan
* @createDate 2019-02-13
* @version 1.0.0
*
*/
@Configuration
public class LoginConfig extends WebSecurityConfigurerAdapter { @Override
protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 设置不需要授权的请求
.antMatchers("/js/*", "/login.html").permitAll() // 其它任何请求都需要验证权限
.anyRequest().authenticated() // 设置自定义表单登录页面
.and().formLogin().loginPage("/login.html") // 设置登录验证请求地址为自定义登录页配置action ("/login/form")
.loginProcessingUrl("/login/form") // 设置默认登录成功跳转页面
.defaultSuccessUrl("/main.html") /* session 管理 */
.and().sessionManagement() // 设置Session失效跳转页面
.invalidSessionUrl("/login.html") // 设置最大Session数为1
.maximumSessions(1) // 设置Session过期处理策略
.expiredSessionStrategy(new SessionInformationExpiredStrategyImpl()).and() // 暂时停用csrf,否则会影响验证
.and().csrf().disable();
} }

8.   SecurityUserDetailsService.java

package com.java.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component; /**
* UserDetailsService实现类
*
* @author Logan
* @createDate 2019-02-13
* @version 1.0.0
*
*/
@Component
public class SecurityUserDetailsService implements UserDetailsService { @Autowired
private PasswordEncoder passwordEncoder; @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 数据库存储密码为加密后的密文(明文为123456)
String password = passwordEncoder.encode("123456"); System.out.println("username: " + username);
System.out.println("password: " + password); // 模拟查询数据库,获取属于Admin和Normal角色的用户
User user = new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("Admin,Normal")); return user;
} }

9.   静态资源文件如下

static/login.html

static/main.html

10.  login.html

<!DOCTYPE html>
<html> <head>
<title>登录</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
</head> <body> <!--登录框-->
<div align="center">
<h2>用户自定义登录页面</h2>
<fieldset style="width: 300px;">
<legend>登录框</legend>
<form action="/login/form" method="post">
<table>
<tr>
<th>用户名:</th>
<td><input name="username" value="Logan" /> </td>
</tr>
<tr>
<th>密码:</th>
<td><input type="password" name="password" value="123456" /> </td>
</tr>
<tr>
<th></th>
<td></td>
</tr>
<tr>
<td colspan="2" align="center"><button type="submit">登录</button></td>
</tr>
</table>
</form>
</fieldset> </div> </body> </html>

11.   main.html

<html>

    <head>
<title>首页</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<script>
window.onclick = function() {
window.open("http://www.cnblogs.com/jonban/");
}
</script>
</head> <body style="background-color: cyan;text-align: center;">
<h1><span style="text-align:center;color:purple;cursor: pointer;">Designed by Logan.</span></h1>
<canvas id="c"></canvas>
<script>
var b = document.body;
var c = document.getElementsByTagName('canvas')[0];
var a = c.getContext('2d');
document.body.clientWidth;
</script> <script>
with(m = Math)
C = cos, S = sin, P = pow, R = random;
c.width = c.height = f = 613;
h = -250; function p(a, b, c) {
if(c > 60)
return [S(a * 7) * (13 + 5 / (.2 + P(b * 4, 4))) - S(b) * 50,
b * f + 50,
625 + C(a * 7) * (13 + 5 / (.2 + P(b * 4, 4))) + b * 400,
a * 1 - b / 2, a
];
A = a * 2 - 1;
B = b * 2 - 1;
if(A * A + B * B < 1) {
if(c > 37) {
n = (j = c & 1) ? 6 : 4;
o = .5 / (a + .01) + C(b * 125) * 3 - a * 300;
w = b * h;
return [o * C(n) + w * S(n) + j * 610 - 390, o * S(n) - w * C(n) + 550 - j * 350, 1180 + C(B + A) * 99 - j * 300, .4 - a * .1 + P(1 - B * B, -h * 6) * .15 - a * b * .4 + C(a + b) / 5 + P(C((o * (a + 1) + (B > 0 ? w : -w)) / 25), 30) * .1 * (1 - B * B), o / 1e3 + .7 - o * w * 3e-6]
}
if(c > 32) {
c = c * 1.16 - .15;
o = a * 45 - 20;
w = b * b * h;
z = o * S(c) + w * C(c) + 620;
return [o * C(c) - w * S(c), 28 + C(B * .5) * 99 - b * b * b * 60 - z / 2 - h, z, (b * b * .3 + P((1 - (A * A)), 7) * .15 + .3) * b, b * .7]
}
o = A * (2 - b) * (80 - c * 2);
w = 99 - C(A) * 120 - C(b) * (-h - c * 4.9) + C(P(1 - b, 7)) * 50 + c * 2;
z = o * S(c) + w * C(c) + 700;
return [o * C(c) - w * S(c), B * 99 - C(P(b, 7)) * 50 - c / 3 - z / 1.35 + 450, z, (1 - b / 1.2) * .9 + a * .1, P((1 - b), 20) / 4 + .05]
}
}
setInterval('for(i=0;i<1e4;i++)if(s=p(R(),R(),i%46/.74)){z=s[2];x=~~(s[0]*f/z-h);y=~~(s[1]*f/z-h);if(!m[q=y*f+x]|m[q]>z)m[q]=z,a.fillStyle="rgb("+~(s[3]*h)+","+~(s[4]*h)+","+~(s[3]*s[3]*-80)+")",a.fillRect(x,y,1,1)}', 0)
</script>
</body> </html>

12.   运行SessionStarter.java ,启动项目

浏览器输入 http://localhost:8080/main.html

自动跳转到登录页面

输入如下信息:

User:Logan

Password:123456

单击【登录】按钮,自动跳转到首页。

换用其它浏览器再次访问 http://localhost:8080/main.html

输入相同用户信息:

User:Logan

Password:123456

单击【登录】按钮,自动跳转到首页。

刷新第一个浏览器,提示

你的账号在另一地点被登录

再次刷新,跳转到登录页面。

Session配置生效!

.

最新文章

  1. Fedora 23 忘记root密码
  2. html、url、http、servlet&amp;jsp之间千丝万缕的联系
  3. 深入理解JPEG图像格式Jphide隐写
  4. java的RMI(Remote Method Invocation)
  5. 实战Django:官方实例Part6
  6. Jquery 和 Js
  7. (转载)PHP静态方法
  8. PYTHON简介及安装
  9. [译]ava 设计模式之享元
  10. yum 安装redis 及简单命令(推荐测试环境,安装简单)
  11. eclipse:Workspace in use or cannot be created
  12. Docker系统五:Docker仓库
  13. 人工智能之基于face_recognition的人脸检测与识别
  14. Docker----起步(2)----在Ubuntu上安装最新版的Docker CE
  15. python基础—sys与os库
  16. Android 6.0 动态权限申请注意事项
  17. Electron入门之ipcMain,ipcRenderer
  18. TCP拥塞控制-慢启动、拥塞避免、快重传、快启动
  19. Linux基础命令---diffstat
  20. NFS常用命令

热门文章

  1. sqlt 之 分析 DB upgrade 导致SQL 性能下降 的方法 xplore
  2. Windows屏幕模糊,图片打开慢等解决方案
  3. Hive学习(一)
  4. cpp 学习笔记
  5. [转]Jquery Mobile dialog的生命周期
  6. Javascript 对象(Object)
  7. 弹性布局 Flexible Box
  8. Python数据报协议以及sockersever模块的使用
  9. 移动webApp - 1像素实现(点5像素的秘密)
  10. git 推送代码到远程端