SpringBoot实现单点登录及权限认证
Spring security+Jwt实现单点登录+权限认证主要技术:配置文件:pom.xml全局配置文件:项目结构图:项目构造:创建项目:初始项目结构引入tomcat容器引入maven组件引入springboot需要的jar包创建启动类:启动项目:创建测试类访问项目/test引入数据库,主要技术:前端:jquery;layui;thymeleaf后端:Spring Boot;maven;Spri
Spring security+Jwt实现单点登录+权限认证
项目地址以上传到码云
地址: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
更多推荐
所有评论(0)