项目地址以上传到码云

地址:https://gitee.com/chen909396/test

主要技术:

  • 前端:jquery;layui;thymeleaf
  • 后端:Spring Boot;maven;Spring Security;Mysql+Jwt

配置文件:

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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>sso</groupId>
	<artifactId>sso_test1</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>sso_test1 Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<!-- 核心配置 -->
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<!-- 管理bean -->
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.3</version>
		<relativePath />
	</parent>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
		<!--使jsp页面生效,进行访问 -->
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
		</dependency>
		<!-- 启动springboot -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<!-- 去除内嵌tomcat -->
			<exclusions>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-tomcat</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- 导入前端模板 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<!-- 使用security框架 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<!-- lombok依赖,省略了getter、setter方法和log定义 -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<scope>provided</scope>
		</dependency>
		<!--通用mapper,tk.mapper -->
		<dependency>
			<groupId>tk.mybatis</groupId>
			<artifactId>mapper-spring-boot-starter</artifactId>
			<version>1.1.4</version>
		</dependency>
		<!--对数据库的支持 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<!--数据库mysql的支持 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!--json的支持 -->
		<dependency>
			<groupId>net.sf.json-lib</groupId>
			<artifactId>json-lib</artifactId>
			<version>2.4</version>
			<classifier>jdk15</classifier>
		</dependency>
		<!-- JWT token认证 -->
		<dependency>
			<groupId>com.auth0</groupId>
			<artifactId>java-jwt</artifactId>
			<version>3.10.3</version>
		</dependency>
		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt</artifactId>
			<version>0.9.0</version>
		</dependency>
	</dependencies>
	<build>
		<finalName>sso_test1</finalName>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<!-- 设置编码为 UTF-8 -->
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

全局配置文件:

#端口号
server.port=8080
#项目路径
#server.Context-path=/

spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html; charset=utf-8
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=/
spring.thymeleaf.suffix=.html


#SQL配置
spring.jpa.database=MYSQL
spring.datasource.url=jdbc:mysql://localhost/h1s?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
spring.datasource.username =chen
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.initial-size=1
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-active=20
spring.datasource.druid.test-on-borrow=true
spring.datasource.druid.stat-view-servlet.allow=true
mybatis.typeAliasesPackage=com.szch3.test.pojo
mybatis.mapperLocations=classpath:mappers/*.xml
mybatis.configuration.mapUnderscoreToCamelCase=true
mybatis.configuration.cacheEnabled=false

#JWT配置
#jwt.header=Authorization//自定义
#令牌密钥
#jwt.secret=6F3931D35F0395DF82B032B8019AC57D//自定义
# 令牌有效期(默认30分钟)
#jwt.expiration: 30//自定义

项目结构图:

在这里插入图片描述

项目构造:

创建项目:

在这里插入图片描述
在这里插入图片描述

初始项目结构

在这里插入图片描述

引入tomcat容器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

引入maven组件

点击Window>>Preferences
点击完成以后如下:
在这里插入图片描述

在这里插入图片描述

引入springboot需要的jar包

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>2021</groupId>
  <artifactId>security_sso</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>security_sso Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<!--  核心配置 管理bean -->
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.3</version>
		<relativePath />
	</parent>
  <dependencies>
  <!-- 启动springboot -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<!-- 去除内嵌tomcat -->
			<exclusions>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-tomcat</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>security_sso</finalName>
  </build>
</project>

创建启动类:

创建包:com.szch3.sso.web

package com.szch3.sso.web;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/*
 * 科普:@SpringBootApplication
 * springboot1.5之前的版本,
 * @SpringBootConfiguration、@ComponenScan、@Configuration,这三个注解是springboot启动的必要条件
 * @SpringBootConfiguration:负责激活SpringBoot自动装配机制
 * @ComponentScan:激活@Component扫描
 * @Configuration:声明被标注为配置类
 * 这三个注解让我们省去了大量的配置文件,但是在springboot1.5之后的版本基本上就看不到他们了,代替的是@SpringBootApplication,它实现了上面三个注解实现的所有功能
 */
@SpringBootApplication
/**
 * 
 * @author DoubleC
 *
 */
public class WebSatrt {
	public static void main(String[] args) {
		SpringApplication.run(WebSatrt.class, args);
	}
}

启动项目:

出现异常:

org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:162) ~[spring-boot-2.4.3.jar:2.4.3]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:576) ~[spring-context-5.3.4.jar:5.3.4]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:144) ~[spring-boot-2.4.3.jar:2.4.3]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:767) [spring-boot-2.4.3.jar:2.4.3]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) [spring-boot-2.4.3.jar:2.4.3]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:426) [spring-boot-2.4.3.jar:2.4.3]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:326) [spring-boot-2.4.3.jar:2.4.3]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1311) [spring-boot-2.4.3.jar:2.4.3]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300) [spring-boot-2.4.3.jar:2.4.3]
	at com.szch3.sso.web.WebSatrt.main(WebSatrt.java:22) [classes/:na]
Caused by: org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getWebServerFactory(ServletWebServerApplicationContext.java:209) ~[spring-boot-2.4.3.jar:2.4.3]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:179) ~[spring-boot-2.4.3.jar:2.4.3]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:159) ~[spring-boot-2.4.3.jar:2.4.3]
	... 9 common frames omitted

原因分析:

springboot自带的tomcat并没有携带tomcat-embed-jasper的依赖,访问页面发现所有的jsp页面都会被下载而不能被访问

解决方案:

添加tomcat-embed-jasper依赖
<!--使jsp页面生效,进行访问 -->
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
		</dependency>

再次启动:

Action:

Correct the classpath of your application so that it contains a single, compatible version of org.apache.tomcat.util.modeler.Registry

原因分析:

此问题由于版本不兼容导致

解决方案:

去掉tomcat8.0

在这里插入图片描述
去掉以后再次启动
在这里插入图片描述

创建测试类

创建包:com.szch3.sso.test

package com.szch3.sso.test;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
/*
 * @Controller:用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象
 * @RequestMapping:注解可以在控制器类的级别和/或其中的方法的级别上使用。来映射URL 到控制器类,或者是到Controller 控制器的处理方法上。当@RequestMapping 标记在Controller 类上的时候,里面使用@RequestMapping 标记的方法的请求地址都是相对于类上的@RequestMapping 而言的;当Controller 类上没有标记@RequestMapping 注解时,方法上的@RequestMapping 都是绝对路径。这种绝对路径和相对路径所组合成的最终路径都是相对于根路径“/ ”而言的
 * @ResponseBody:将http请求解析为对应的对象
 */
public class Test {
	@RequestMapping("/test")
	@ResponseBody
	public String test() {
		return "test";
	}
}

访问项目/test

localhost:8080/test
在这里插入图片描述

说明:此时启动类和测试类是同一级,浏览器无法解析到/test
解决方案:

#在启动类中添加注解:@ComponentScan("com.szch3")
package com.szch3.sso.web;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
/*
 * 科普:@SpringBootApplication
 * springboot1.5之前的版本,
 * @SpringBootConfiguration、@ComponenScan、@Configuration,这三个注解是springboot启动的必要条件
 * @SpringBootConfiguration:负责激活SpringBoot自动装配机制
 * @ComponentScan:激活@Component扫描
 * @Configuration:声明被标注为配置类
 * 这三个注解让我们省去了大量的配置文件,但是在springboot1.5之后的版本基本上就看不到他们了,代替的是@SpringBootApplication,它实现了上面三个注解实现的所有功能
 */
@ComponentScan("com.szch3")
/*
 * @ComponentScan:将组件自动加载到容器
 * 加了包扫描@ComponentScan注解后,
 * 只要标注了@Controller、@Service、@Repository、@Component注解中的任何一个,
 * 其组件都会被自动扫描,加入到容器中
 */
/**
 * 
 * @author DoubleC
 *
 */
public class WebSatrt {
	public static void main(String[] args) {
		SpringApplication.run(WebSatrt.class, args);
	}
}

访问界面:
在这里插入图片描述

实现Security权限管理认证

导入jar包(pom文件配置)

<!-- 使用security框架 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>

创建security框架

创建包:com.szch3.soo.security

package com.szch3.sso.security;

import org.springframework.context.annotation.Configuration;
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;

@EnableWebSecurity
/*
 * @EnableWebSecurity: 有两个作用: ①:加载了WebSecurityConfiguration配置类,配置安全认证策略。
 * ②:加载了AuthenticationConfiguration,配置了认证信息
 * 该注解是一个组合注解:导入了WebSecurityConfiguration配置类,另外使用了@EnableGlobalAuthentication注解
 * WebSecurityConfiguration:这是Spring Security的核心过滤器,这是请求的认证入口
 * 
 * @EnableGlobalAuthentication:注入了AuthenticationConfiguration配置类,这个类的主要作用是,
 * 想spring容器中注入AuthenticationManagerBuilder,
 * AuthenticationManagerBuider其实使用了建造者模式,他能建造AuthenticationManager,是身份认证的入口
 */
@Configuration // 声明被标注为配置类
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		// TODO Auto-generated method stub
		http
		.formLogin()//使用表单登录
		.loginPage("/login.html")//自定义默认跳转页面
		.permitAll()///声明该界面的所有权(任何人都可以访问)
		.loginProcessingUrl("/user/login")//自定义登录请求接口
		.and().authorizeRequests()
		.antMatchers("/css/**","/js/**","/img/**").permitAll()
		.anyRequest().authenticated();//自定义登录请求接口
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		// TODO Auto-generated method stub
		super.configure(auth);
	}
}

访问效果
在这里插入图片描述
使用用户密+密码+用户权限

package com.szch3.sso.security;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
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.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import com.fasterxml.jackson.databind.ObjectMapper;


@EnableWebSecurity
/*
 * @EnableWebSecurity: 有两个作用: ①:加载了WebSecurityConfiguration配置类,配置安全认证策略。
 * ②:加载了AuthenticationConfiguration,配置了认证信息
 * 该注解是一个组合注解:导入了WebSecurityConfiguration配置类,另外使用了@EnableGlobalAuthentication注解
 * WebSecurityConfiguration:这是Spring Security的核心过滤器,这是请求的认证入口
 * 
 * @EnableGlobalAuthentication:注入了AuthenticationConfiguration配置类,这个类的主要作用是,
 * 想spring容器中注入AuthenticationManagerBuilder,
 * AuthenticationManagerBuider其实使用了建造者模式,他能建造AuthenticationManager,是身份认证的入口
 */
@Configuration // 声明被标注为配置类
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		// TODO Auto-generated method stub
		http.csrf().disable()//关闭CSRF  CSRF:Cross-site request forgery跨站请求伪造,也被称为“one click attack”或者session riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。
		.formLogin()//使用表单登录
		.loginPage("/login.html")//自定义默认跳转页面
		.permitAll()///声明该界面的所有权(任何人都可以访问)
		.loginProcessingUrl("/user/login")//自定义登录请求接口
		.usernameParameter("phone")
		.passwordParameter("pass")
		/*
		 * 请求成功处理
		 */
		.successHandler(new AuthenticationSuccessHandler() {
			@Override
			public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
					Authentication auth) throws IOException, ServletException {
				// TODO Auto-generated method stub
				Object principal = auth.getPrincipal();
				
				// 封装自定义返回用户信息
				// 封装自定义返回用户信息
			
				PrintWriter out = response.getWriter();
				response.setStatus(200);
				Map<String, Object> map = new HashMap<>();
				map.put("status", 200);
				map.put("msg", principal);
				ObjectMapper om = new ObjectMapper();
				out.write(om.writeValueAsString(map));
				out.flush();
				out.close();
			}
		})
		/*
		 * 请求异常处理
		 */
		.failureHandler(new AuthenticationFailureHandler() {
			
			@Override
			public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
					AuthenticationException e) throws IOException, ServletException {
				// TODO Auto-generated method stub
				response.setContentType("application/json;charset=utf-8");
				
				PrintWriter out = response.getWriter();
				response.setStatus(401);
				Map<Object, Object> map = new HashMap<>();
				map.put("status", 401);
				if (e instanceof LockedException) {
					map.put("msg", "账户被锁定,登录失败!");
				} else if (e instanceof BadCredentialsException) {
					map.put("msg", "账户或密码输入错误,登录失败!");
				} else if (e instanceof DisabledException) {
					map.put("msg", "账户被禁用,登录失败");
				} else if (e instanceof AccountExpiredException) {
					map.put("msg", "账户已过期,登录失败");
				} else if (e instanceof CredentialsExpiredException) {
					map.put("msg", "密码已过期,登录失败");
				}else if(e instanceof CredentialsExpiredException ) {
					map.put("msg", "token以失效,请重新登录");
				} else {
					map.put("msg", "登录失败");
				}
				ObjectMapper om = new ObjectMapper();
				out.write(om.writeValueAsString(map));
				out.flush();
				out.close();
			}
		})
		.and().authorizeRequests()
		.antMatchers("/css/**","/js/**","/img/**").permitAll()
		.anyRequest().authenticated();//自定义登录请求接口
		http.headers().cacheControl();//禁用缓存
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		// TODO Auto-generated method stub
		auth.inMemoryAuthentication()
			.withUser("15671366239")//用户账号
			.password(passwordEncoder().encode("123456"))//用户密码
			.authorities("user","admin")//用户的权限
			.and().passwordEncoder(passwordEncoder());//配置BCrypt加密
	}
	/**
	 * 将密码进行加密
	 * @return
	 */
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
}

前端ajax
此处使用了layui框架

	$.ajax({
						url:"/user/login",
						method:"post",
						data:{
							"phone":phone,
							"pass":pass
						},
						dataType:"json",
						
						success:function(result){
							if(result.data="200"){
								/*
								* 后期整合jwt的时候使用
								*var token =result.token;
								*createCookie("token",token,3);
								*/
								window.location.href="/index.html";
								return true;
							}else{
								
								return false;
							}
						},
						error:function(xmlHttpRequest, errorThrown){
							var str = JSON.stringify(xmlHttpRequest.responseJSON); 
							alert(str);
							var obj = JSON.parse(str); //由JSON字符串转换为JSON对象
							if(obj.status==401){
								$('.user-pass-err').removeClass('hide').find("em").text(obj.msg);
								/*layui.use('layer', function() {
									var layer = layui.layer;
									layer.msg(obj.msg, {
										icon: 2,
										time: 2000 //2秒关闭(如果不配置,默认是3秒)
									});
								});*/
								return false;
							}else{
								layui.use('layer', function() {
									var layer = layui.layer;
									layer.msg("网络异常", {
										icon: 2,
										time: 1000 //2秒关闭(如果不配置,默认是3秒)
									});
									//layer.msg('账号不能为空');
								});
								return false;
							}
						}
					});

整合JWT,实现单点登录

在全局配置中添加数据库

#SQL配置
spring.jpa.database=MYSQL
spring.datasource.url=jdbc:mysql://localhost/h1s?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
spring.datasource.username =chen
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.initial-size=1
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-active=20
spring.datasource.druid.test-on-borrow=true
spring.datasource.druid.stat-view-servlet.allow=true
mybatis.typeAliasesPackage=com.szch3.test.pojo
mybatis.mapperLocations=classpath:mappers/*.xml
mybatis.configuration.mapUnderscoreToCamelCase=true
mybatis.configuration.cacheEnabled=false

添加Mysql依赖

<!--通用mapper,tk.mapper -->
		<dependency>
			<groupId>tk.mybatis</groupId>
			<artifactId>mapper-spring-boot-starter</artifactId>
			<version>1.1.4</version>
		</dependency>
		<!--对数据库的支持 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<!--数据库mysql的支持 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

生成jwt的时候需要用户信息

引入lombok依赖(可以不添加.不添加就手写get() set()方法)

<!-- lombok依赖,省略了getter、setter方法和log定义 -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<scope>provided</scope>
		</dependency>

生成一个用户信息实体类

package com.szch3.sso.pojo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;


import lombok.Data;
@Data
public class User  implements UserDetails{
	private String username;//用户账号
	private String password;//用户密码
	private boolean locked;//锁定
	private boolean enabled;//禁用
	private List<Roles> roles;
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		// TODO Auto-generated method stub
		List<SimpleGrantedAuthority> authorities = new ArrayList<>();
		for(Roles role:roles) {
			authorities.add(new SimpleGrantedAuthority(role.getName()));
		}
		return authorities;
	}
	@Override
	public boolean isAccountNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}
	@Override
	public boolean isAccountNonLocked() {
		// TODO Auto-generated method stub
		return !locked;
	}
	@Override
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}
}

生成权限实体类

ackage com.szch3.test.pojo;

import lombok.Data;

@Data
public class Roles {
	private int id;//权限ID
	private String name;//权限类
	private String namezh;//权限说明
}

导入:jwt依赖包

<!-- JWT token依赖包 -->
		<dependency>
			<groupId>com.auth0</groupId>
			<artifactId>java-jwt</artifactId>
			<version>3.10.3</version>
		</dependency>
		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt</artifactId>
			<version>0.9.0</version>
		</dependency>

先创建工具类:JwtTokenUtil

创建包:com.szch3.sso.util

package com.szch3.sso.util;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import com.szch3.sso.pojo.User;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@Component
public class JwtTokenUtil implements Serializable {
	private static String secret = "6F3931D35F0395DF82B032B8019AC57D";
	// 毫秒
	//private static Long expiration = 1000l * 5;// 五秒
	private static Long expiration = 1000l*60*60*24*7 ;//七天
	private static String header = "Authorization";

	/**
	 * 从数据声明生成令牌
	 * 
	 * @param claims 数据声明
	 * @return 令牌
	 */
	public static String generateToken(Map<String, Object> claims) {
		Date expirationDate = new Date(System.currentTimeMillis() + expiration);
		return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret)
				.compact();
	}

	/**
	 * 从令牌中获取数据声明
	 * 
	 * @param token 令牌
	 * @return 数据声明
	 */
	private static Claims getClaimsFromToken(String token) {
		Claims claims;
		try {
			claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
		} catch (Exception e) {
			// TODO: handle exception
			claims = null;
		}
		return claims;
	}

	/**
	 * 生成令牌
	 * 
	 * @param userDetails 用户
	 * @return
	 */
	public static String generateToken(User user) {
		Map<String, Object> claims = new HashMap<>();
		claims.put("sub", user.getUsername());
		claims.put("authorities", user.getAuthorities());
		return generateToken(claims);
	}

	/**
	 * 从令牌中获取用户名
	 * 
	 * @param token 令牌
	 * @return 用户名
	 */
	public static String getUsernameFromToken(String token) {
		String username;
		try {
			Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
			// Claims claims=getClaimsFromToken(token);
			username = claims.getSubject();
			String authority = claims.get("authorities").toString();
		} catch (Exception e) {
			// TODO: handle exception
			username = null;
			//throw new CredentialsExpiredException ("Token已失效");
		}
		return username;
	}

	/**
	 * 判断令牌是否过期
	 * 
	 * @param token令牌
	 * @return 是否过期
	 */
	public static Boolean isTokenExpired(String token) {
		try {
			Claims claims = getClaimsFromToken(token);
			Date expiration = claims.getExpiration();
			return expiration.before(new Date());
		} catch (Exception e) {
			// generateToken
			// TODO: handle exception
			// throw new AccountExpiredException("token已失效");
			return true;
		}
	}

	public static String refreshToken(String token) {
		String refreshedToken;
		try {
			final Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
			refreshedToken = generateToken(claims);
		} catch (Exception e) {
			refreshedToken = null;
			 //throw new BadCredentialsException("验证码不正确");
		}
		return refreshedToken;
	}

	public static Boolean validateToken(String token, UserDetails userDetails) {
		User user = (User) userDetails;
		String username = getUsernameFromToken(token);
		return (username.equals(user.getUsername()) && !isTokenExpired(token));
	}

	public String getSecret() {
		return secret;
	}

	public void setSecret(String secret) {
		this.secret = secret;
	}

	public static Long getExpiration() {
		return expiration;
	}

	public void setExpiration(Long expiration) {
		this.expiration = expiration;
	}

	public static String getHeader() {
		return header;
	}

	public void setHeader(String header) {
		this.header = header;
	}

}

准备工作完成,接下来创建token

修改启动类(WebStart.class)

在启动类上面添加注解

@MapperScan("com.szch3.sso.mapper")
作用:指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类
添加位置:是在Springboot启动类上面添加,
package com.szch3.sso.web;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
/*
 * 科普:@SpringBootApplication
 * springboot1.5之前的版本,
 * @SpringBootConfiguration、@ComponenScan、@Configuration,这三个注解是springboot启动的必要条件
 * @SpringBootConfiguration:负责激活SpringBoot自动装配机制
 * @ComponentScan:激活@Component扫描
 * @Configuration:声明被标注为配置类
 * 这三个注解让我们省去了大量的配置文件,但是在springboot1.5之后的版本基本上就看不到他们了,代替的是@SpringBootApplication,它实现了上面三个注解实现的所有功能
 */
@ComponentScan("com.szch3")
/*
 * @ComponentScan:将组件自动加载到容器
 * 加了包扫描@ComponentScan注解后,
 * 只要标注了@Controller、@Service、@Repository、@Component注解中的任何一个,
 * 其组件都会被自动扫描,加入到容器中
 */
@MapperScan("com.szch3.sso.mapper")
/*
 * 作用:指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类
 * 添加位置:是在Springboot启动类上面添加
 * 使用 @Mapper,最终 Mybatis 会有一个拦截器,会自动的把 @Mapper 注解的接口生成动态代理类。这点可以在 MapperRegistry 类中的源代码中查看。
 * @Mapper 注解针对的是一个一个的类,相当于是一个一个 Mapper.xml 文件。
 * 而一个接口一个接口的使用 @Mapper,太麻烦了,于是 @MapperScan 就应用而生了。
 * @MapperScan 配置一个或多个包路径,自动的扫描这些包路径下的类,自动的为它们生成代理类。
 */
/**
 * 
 * @author DoubleC
 *
 */
public class WebSatrt {
	public static void main(String[] args) {
		SpringApplication.run(WebSatrt.class, args);
	}
}

创建cookie工具类

package com.szch3.sso.util;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CookieUtils {
	final static Logger logger = LoggerFactory.getLogger(CookieUtils.class);
	
	/**
	 * 
	 * @Description: 得到Cookie的值, 不编码
	 * @param request
	 * @param cookieName
	 * @return
	 */
    public static String getCookieValue(HttpServletRequest request, String cookieName) {
        return getCookieValue(request, cookieName, false);
    }
    
    /**
     * 
     * @Description: 得到Cookie的值
     * @param request
     * @param cookieName
     * @param isDecoder
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || cookieName == null) {
            return null;
        }
        String retValue = null;
        try {
            for (int i = 0; i < cookieList.length; i++) {
                if (cookieList[i].getName().equals(cookieName)) {
                    if (isDecoder) {
                        retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
                    } else {
                        retValue = cookieList[i].getValue();
                    }
                    break;
                }
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return retValue;
    }
 
    /**
     * 
     * @Description: 得到Cookie的值
     * @param request
     * @param cookieName
     * @param encodeString
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || cookieName == null) {
            return null;
        }
        String retValue = null;
        try {
            for (int i = 0; i < cookieList.length; i++) {
                if (cookieList[i].getName().equals(cookieName)) {
                    retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
                    break;
                }
            }
        } catch (UnsupportedEncodingException e) {
        	 e.printStackTrace();
        }
        return retValue;
    }
 
    /**
     * 
     * @Description: 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码
     * @param request
     * @param response
     * @param cookieName
     * @param cookieValue
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
            String cookieValue) {
        setCookie(request, response, cookieName, cookieValue, -1);
    }
 
    /**
     * 
     * @Description: 设置Cookie的值 在指定时间内生效,但不编码
     * @param request
     * @param response
     * @param cookieName
     * @param cookieValue
     * @param cookieMaxage
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
            String cookieValue, int cookieMaxage) {
        setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);
    }
 
    /**
     * 
     * @Description: 设置Cookie的值 不设置生效时间,但编码
     * 在服务器被创建,返回给客户端,并且保存客户端
     * 如果设置了SETMAXAGE(int seconds),会把cookie保存在客户端的硬盘中
     * 如果没有设置,会默认把cookie保存在浏览器的内存中
     * 一旦设置setPath():只能通过设置的路径才能获取到当前的cookie信息
     * @param request
     * @param response
     * @param cookieName
     * @param cookieValue
     * @param isEncode
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
            String cookieValue, boolean isEncode) {
        setCookie(request, response, cookieName, cookieValue, -1, isEncode);
    }
 
   /**
    * 
    * @Description: 设置Cookie的值 在指定时间内生效, 编码参数
    * @param request
    * @param response
    * @param cookieName
    * @param cookieValue
    * @param cookieMaxage
    * @param isEncode
    */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
            String cookieValue, int cookieMaxage, boolean isEncode) {
        doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);
    }
 
    /**
     * 
     * @Description: 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)
     * @param request
     * @param response
     * @param cookieName
     * @param cookieValue
     * @param cookieMaxage
     * @param encodeString
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
            String cookieValue, int cookieMaxage, String encodeString) {
        doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);
    }
 
    /**
     * 
     * @Description: 删除Cookie带cookie域名
     * @param request
     * @param response
     * @param cookieName
     */
    public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,
            String cookieName) {
        doSetCookie(request, response, cookieName, null, -1, false);
//        doSetCookie(request, response, cookieName, "", -1, false);
    }
 
    
    /**
     * 
     * @Description: 设置Cookie的值,并使其在指定时间内生效
     * @param request
     * @param response
     * @param cookieName
     * @param cookieValue
     * @param cookieMaxage	cookie生效的最大秒数
     * @param isEncode
     */
    private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
            String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
        try {
            if (cookieValue == null) {
                cookieValue = "";
            } else if (isEncode) {
                cookieValue = URLEncoder.encode(cookieValue, "utf-8");
            }
            Cookie cookie = new Cookie(cookieName, cookieValue);
            if (cookieMaxage > 0)
                cookie.setMaxAge(cookieMaxage);
            if (null != request) {// 设置域名的cookie
            	String domainName = getDomainName(request);
                logger.info("========== domainName: {} ==========", domainName);
                if (!"localhost".equals(domainName)) {
                	cookie.setDomain(domainName);
                }
            }
            cookie.setPath("/");
            response.addCookie(cookie);
        } catch (Exception e) {
        	 e.printStackTrace();
        }
    }
 
    /**
     * 
     * @Description: 设置Cookie的值,并使其在指定时间内生效
     * @param request
     * @param response
     * @param cookieName
     * @param cookieValue
     * @param cookieMaxage	cookie生效的最大秒数
     * @param encodeString
     */
    private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
            String cookieName, String cookieValue, int cookieMaxage, String encodeString) {
        try {
            if (cookieValue == null) {
                cookieValue = "";
            } else {
                cookieValue = URLEncoder.encode(cookieValue, encodeString);
            }
            Cookie cookie = new Cookie(cookieName, cookieValue);
            if (cookieMaxage > 0)
                cookie.setMaxAge(cookieMaxage);
            if (null != request) {// 设置域名的cookie
            	String domainName = getDomainName(request);
                logger.info("========== domainName: {} ==========", domainName);
                if (!"localhost".equals(domainName)) {
                	cookie.setDomain(domainName);
                }
            }
            cookie.setPath("/");
            response.addCookie(cookie);
        } catch (Exception e) {
        	 e.printStackTrace();
        }
    }
 
    /**
     * 
     * @Description: 得到cookie的域名
     * @return
     */
    private static final String getDomainName(HttpServletRequest request) {
        String domainName = null;
 
        String serverName = request.getRequestURL().toString();
        if (serverName == null || serverName.equals("")) {
            domainName = "";
        } else {
            serverName = serverName.toLowerCase();
            serverName = serverName.substring(7);
            final int end = serverName.indexOf("/");
            serverName = serverName.substring(0, end);
            if (serverName.indexOf(":") > 0) {
            	String[] ary = serverName.split("\\:");
            	serverName = ary[0];
            }
 
            final String[] domains = serverName.split("\\.");
            int len = domains.length;
            if (len > 3 && !isIp(serverName)) {
            	// www.xxx.com.cn
                domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
            } else if (len <= 3 && len > 1) {
                // xxx.com or xxx.cn
                domainName = "." + domains[len - 2] + "." + domains[len - 1];
            } else {
                domainName = serverName;
            }
        }
        return domainName;
    }
    
    public static String trimSpaces(String IP){//去掉IP字符串前后所有的空格  
        while(IP.startsWith(" ")){  
               IP= IP.substring(1,IP.length()).trim();  
            }  
        while(IP.endsWith(" ")){  
               IP= IP.substring(0,IP.length()-1).trim();  
            }  
        return IP;  
    }  
    
    public static boolean isIp(String IP){//判断是否是一个IP  
        boolean b = false;  
        IP = trimSpaces(IP);  
        if(IP.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")){  
            String s[] = IP.split("\\.");  
            if(Integer.parseInt(s[0])<255)  
                if(Integer.parseInt(s[1])<255)  
                    if(Integer.parseInt(s[2])<255)  
                        if(Integer.parseInt(s[3])<255)  
                            b = true;  
        }  
        return b;  
    }  

}

创建过滤器

添加json依赖

<!--json的支持 -->
		<dependency>
			<groupId>net.sf.json-lib</groupId>
			<artifactId>json-lib</artifactId>
			<version>2.4</version>
			<classifier>jdk15</classifier>
		</dependency>

创建包:com.szch3.sso.service

package com.szch3.sso.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import com.szch3.sso.mapper.UserMapper;
import com.szch3.sso.pojo.Roles;
import com.szch3.sso.pojo.User;

@Service
public class UserService implements UserDetailsService{
	@Autowired
	private UserMapper userMapper;
	@SuppressWarnings("rawtypes")
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// TODO Auto-generated method stub
		System.out.println("ss:"+username);
		User user = new User();
		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
		String password = encoder.encode("123456");
		user.setUsername(username);
		user.setPassword(password);
		user.setEnabled(true);
		List<Roles> arrayList = new ArrayList();
		Roles roles = new Roles();
		roles.setName("user");
		roles.setNamezh("普通会员");
		arrayList.add(roles);
		user.setRoles(arrayList);
		System.out.println(user.toString());
		return user;
	}

}

创建包:com.szch3.sso.util

package com.szch3.sso.util;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import com.szch3.sso.pojo.User;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@Component
public class JwtTokenUtil implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private static String secret = "6F3931D35F0395DF82B032B8019AC57D";
	// 毫秒
	//private static Long expiration = 1000l * 5;// 五秒
	private static Long expiration = 1000l*60*60*24*7 ;//七天
	private static String header = "Authorization";

	/**
	 * 从数据声明生成令牌
	 * 
	 * @param claims 数据声明
	 * @return 令牌
	 */
	public static String generateToken(Map<String, Object> claims) {
		Date expirationDate = new Date(System.currentTimeMillis() + expiration);
		return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret)
				.compact();
	}

	/**
	 * 从令牌中获取数据声明
	 * 
	 * @param token 令牌
	 * @return 数据声明
	 */
	private static Claims getClaimsFromToken(String token) {
		Claims claims;
		try {
			claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
		} catch (Exception e) {
			// TODO: handle exception
			claims = null;
		}
		return claims;
	}

	/**
	 * 生成令牌
	 * 
	 * @param userDetails 用户
	 * @return
	 */
	public static String generateToken(User user) {
		Map<String, Object> claims = new HashMap<>();
		claims.put("sub", user.getUsername());
		claims.put("authorities", user.getAuthorities());
		return generateToken(claims);
	}

	/**
	 * 从令牌中获取用户名
	 * 
	 * @param token 令牌
	 * @return 用户名
	 */
	public static String getUsernameFromToken(String token) {
		String username;
		try {
			Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
			// Claims claims=getClaimsFromToken(token);
			username = claims.getSubject();
			String authority = claims.get("authorities").toString();
		} catch (Exception e) {
			// TODO: handle exception
			username = null;
			//throw new CredentialsExpiredException ("Token已失效");
		}
		return username;
	}

	/**
	 * 判断令牌是否过期
	 * 
	 * @param token令牌
	 * @return 是否过期
	 */
	public static Boolean isTokenExpired(String token) {
		try {
			Claims claims = getClaimsFromToken(token);
			Date expiration = claims.getExpiration();
			return expiration.before(new Date());
		} catch (Exception e) {
			// generateToken
			// TODO: handle exception
			// throw new AccountExpiredException("token已失效");
			return true;
		}
	}

	public static String refreshToken(String token) {
		String refreshedToken;
		try {
			final Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
			refreshedToken = generateToken(claims);
		} catch (Exception e) {
			refreshedToken = null;
			 //throw new BadCredentialsException("验证码不正确");
		}
		return refreshedToken;
	}

	public static Boolean validateToken(String token, UserDetails userDetails) {
		User user = (User) userDetails;
		String username = getUsernameFromToken(token);
		return (username.equals(user.getUsername()) && !isTokenExpired(token));
	}

	public String getSecret() {
		return secret;
	}

	public void setSecret(String secret) {
		this.secret = secret;
	}

	public static Long getExpiration() {
		return expiration;
	}

	public void setExpiration(Long expiration) {
		this.expiration = expiration;
	}

	public static String getHeader() {
		return header;
	}

	public void setHeader(String header) {
		this.header = header;
	}

}

创建包:com.szch3.sso.security.filter

package com.szch3.sso.security.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import com.szch3.sso.service.UserService;
import com.szch3.sso.util.CookieUtils;
import com.szch3.sso.util.JwtTokenUtil;


public class JwtAuthenticationTokenFilter extends OncePerRequestFilter{
	@Autowired 
	private UserService userService;
	@Autowired
	private JwtTokenUtil jwtTokenUtil;
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		// TODO Auto-generated method stub
		String token = CookieUtils.getCookieValue(request, "token");
        if (token != null && StringUtils.isNotEmpty(token)) {
			/**
			 * 引起缓存以后实现该功能
			 * 判断token是否正常
			 * 不正常--刷新token;正常--返回token
			 */
        	//token = jwtTokenUtil.refreshToken(token);
        	/**
        	 * 待实现代码
        	 */
        	//获取token中的用户信息
            String username = jwtTokenUtil.getUsernameFromToken(token);
           //判断浏览器中是否携带了用户的授权信息,如果为空,表示未携带,需要授权
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            	UserDetails userDetails = this.userService.loadUserByUsername(username);
                if (jwtTokenUtil.validateToken(token, userDetails)) {
					UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
        }
        filterChain.doFilter(request, response);
	}

}

修改权限控制中心(WebSecurityConfig.class)

修改权限控制中心(WebSecurityConfig.class)
如下:

package com.szch3.sso.security;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
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.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.szch3.sso.pojo.User;
import com.szch3.sso.security.filter.JwtAuthenticationTokenFilter;
import com.szch3.sso.service.UserService;
import com.szch3.sso.util.JwtTokenUtil;


@EnableWebSecurity
/*
 * @EnableWebSecurity: 有两个作用: ①:加载了WebSecurityConfiguration配置类,配置安全认证策略。
 * ②:加载了AuthenticationConfiguration,配置了认证信息
 * 该注解是一个组合注解:导入了WebSecurityConfiguration配置类,另外使用了@EnableGlobalAuthentication注解
 * WebSecurityConfiguration:这是Spring Security的核心过滤器,这是请求的认证入口
 * 
 * @EnableGlobalAuthentication:注入了AuthenticationConfiguration配置类,这个类的主要作用是,
 * 想spring容器中注入AuthenticationManagerBuilder,
 * AuthenticationManagerBuider其实使用了建造者模式,他能建造AuthenticationManager,是身份认证的入口
 */
@Configuration // 声明被标注为配置类
public class WebSecurityConfig<V> extends WebSecurityConfigurerAdapter {
	@Autowired
	private UserService userService;
	@Autowired 
	private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		// TODO Auto-generated method stub
		http.csrf().disable()//关闭CSRF  CSRF:Cross-site request forgery跨站请求伪造,也被称为“one click attack”或者session riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。
		.formLogin()//使用表单登录
		.loginPage("/login.html")//自定义默认跳转页面
		.permitAll()///声明该界面的所有权(任何人都可以访问)
		.loginProcessingUrl("/user/login")//自定义登录请求接口
		.usernameParameter("phone")
		.passwordParameter("pass")
		/*
		 * 请求成功处理
		 */
		.successHandler(new AuthenticationSuccessHandler() {
			@Override
			public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
					Authentication auth) throws IOException, ServletException {
				// TODO Auto-generated method stub
				User user =  (User) auth.getPrincipal();
				//生成token
				String token = JwtTokenUtil.generateToken(user);
				//封装信息返回给前端
				PrintWriter out = response.getWriter();
				response.setStatus(200);
				Map<String, Object> map = new HashMap<>();
				map.put("status", 200);
				map.put("msg", user);
				map.put("token",token);
				ObjectMapper om = new ObjectMapper();
				out.write(om.writeValueAsString(map));
				out.flush();
				out.close();
			}
		})
		/*
		 * 请求异常处理
		 */
		.failureHandler(new AuthenticationFailureHandler() {
			
			@Override
			public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
					AuthenticationException e) throws IOException, ServletException {
				// TODO Auto-generated method stub
				response.setContentType("application/json;charset=utf-8");
				
				PrintWriter out = response.getWriter();
				response.setStatus(401);
				Map<Object, Object> map = new HashMap<>();
				map.put("status", 401);
				if (e instanceof LockedException) {
					map.put("msg", "账户被锁定,登录失败!");
				} else if (e instanceof BadCredentialsException) {
					map.put("msg", "账户或密码输入错误,登录失败!");
				} else if (e instanceof DisabledException) {
					map.put("msg", "账户被禁用,登录失败");
				} else if (e instanceof AccountExpiredException) {
					map.put("msg", "账户已过期,登录失败");
				} else if (e instanceof CredentialsExpiredException) {
					map.put("msg", "密码已过期,登录失败");
				}else if(e instanceof CredentialsExpiredException ) {
					map.put("msg", "token以失效,请重新登录");
				} else {
					map.put("msg", "登录失败");
				}
				ObjectMapper om = new ObjectMapper();
				out.write(om.writeValueAsString(map));
				out.flush();
				out.close();
			}
		})
		//.and().headers().frameOptions().disable()//防止跨域
		.and().authorizeRequests()
		.antMatchers("/css/**","/js/**","/img/**").permitAll()
		.anyRequest().authenticated();//自定义登录请求接口
		http.headers().cacheControl();//禁用缓存
		//禁止使用new JwtAuthenticationTokenFilter()
		http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		// TODO Auto-generated method stub
		auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
	}
	/**
	 * 将密码进行加密
	 * @return
	 */
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
}

创建token

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐