环境:JDK1.8 、MAVEN 3.6.1 、eclipse
1.在SpringBoot中添加SpringSecurity的支持
当前项目中的
pom
文件
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.4.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 加载spring security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 加载thymeleaf模板 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
2.编写模板index.html页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Insert title here</title>
</head>
<body>
welcome to index html!
</body>
</html>
3.简单的启用使用@EnableGlobalMethodSecurity开启
此时的入口类
Application
类中文件内容:
@EnableGlobalMethodSecurity
@Controller
@SpringBootApplication
public class Application {
@RequestMapping("/index")
public String index() {
return "index";
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4.测试@EnableGlobalMethodSecurity开启的安全校验
发现控制台打印了密码(需要log级别为info打印,否者不会显示
),用户名默认为:user
结果:成功登录,并显示了页面
5.使用配置文件的方式配置用户名和密码
当前可以使用的SECURITY的属性如下(配置文件中的内容)
SECURITY (SecurityProperties)
security.basic.authorize-mode=role
# Security authorize mode to apply.
security.basic.enabled=true
# Enable basic authentication.
security.basic.path=/**
# Comma-separated list of paths to secure.
security.basic.realm=Spring
# HTTP basic realm name.
security.enable-csrf=false
# Enable Cross Site Request Forgery support.
security.filter-order=0
# Security filter chain order.
security.filter-dispatcher-types=ASYNC, FORWARD, INCLUDE, REQUEST
# Security filter chain dispatcher types.
security.headers.cache=true
# Enable cache control HTTP headers.
security.headers.content-security-policy=
# Value for content security policy header.
security.headers.content-security-policy-mode=default
# Content security policy mode.
security.headers.content-type=true
# Enable “X-Content-Type-Options” header.
security.headers.frame=true
# Enable “X-Frame-Options” header.
security.headers.hsts=all
# HTTP Strict Transport Security (HSTS) mode (none, domain, all).
security.headers.xss=true
# Enable cross site scripting (XSS) protection.
security.ignored=
# Comma-separated list of paths to exclude from the default secured paths.
security.require-ssl=false
# Enable secure channel for all requests.
security.sessions=stateless
# Session creation policy (always, never, if_required, stateless).
security.user.name=user
# Default user name.
security.user.password=
# Password for the default user name. A random password is logged on startup by default.
security.user.role=USER
# Granted roles for the default user name.
当前
application.properties
文件中的内容可以如下:
security.user.name=admin
security.user.password=123456
6.测试当前设置的用户名和密码
结果成功
7.简单的启用使用@EnableWebSecurity开启
此时的
Application
入口类的内容:
@Controller
@SpringBootApplication
@EnableWebSecurity
public class Application {
@RequestMapping("/index")
public String index() {
return "index";
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
8.测试@EnableWebSecurity开启的安全校验
发现出错:SpringSecurity的web登录校验,发现启动出错,说没有一个实现WebSecurityConfigurer的@Bean被注册,当使用@EnableWebSecurity注解的时候,
//Hint try extending WebSecurityConfigurerAdapter ,尝试继承WebSecurityConfigurerAdapter来解决问题
9.创建自定义的WebSecurityConfigurerAdapter
例如创建
ApplicationWebSecurityConfigurer
类,其内容如下
/**
* @description 使用@EnableWebSecurity的時候需要自定义安全校验器
* @author hy
* @date 2019-08-11
*/
@Component
public class ApplicationWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
/**
* @descriptoin 通过springboot的官方文档可以知道
* 默认设置AuthenticationManager只有一个用户('用户'用户名和随机密码,在应用程序启动时以INFO级别打印)
* 通过AuthenticationManager理论上可以控制用户校验管理
* 使用自定义的令牌可以实现访问jdbc和其他的设置
*/
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return new MyAuthenticationManager();// 返回自定义的用户管理
}
//配置内存中的用户,此配置与上面的自定义的用户令牌冲突,只能存在一个,当authenticationManager() 存在的时候这个不起作用
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("123456").roles("User");
}
//配置Http安全校的路径
@Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
super.configure(http);
}
}
10.编写自定义的AuthenticationManager完成用户令牌管理
其中的
MyAuthenticationManager
类的内容如下
/**
- @description 我的用戶安全管理器
- @author hy
- @date 2019-08-11
*/
public class MyAuthenticationManager implements AuthenticationManager {
//创建自定义的令牌存储器
private static Map<String, String> myTockenMap = new HashMap<String, String>();
public MyAuthenticationManager() {
System.out.println("自定义用戶验证管理器被实例化。。。。。");
}
/**
* @description 通过查看结构树可以看到,我们可以使用UsernamePasswordAuthenticationToken这个来实现用户的登录控制
* UsernamePasswordAuthenticationToken 用户名密码的令牌
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
return myAuthenticate(authentication);
}
public Authentication systemAuthenticate(Authentication authentication) throws AuthenticationException {
// 获取传递的参数:登录的用户名和登录的密码
System.out.println("用户名:" + authentication.getName());// 等同于authentication.getPrincipal()
System.out.println(authentication);
// UsernamePasswordAuthenticationToken@53f00533发现当前的authentication其实就是UsernamePasswordAuthenticationToken对象
System.out.println("密碼:" + authentication.getCredentials());
System.out.println("isAuthenticated():" + authentication.isAuthenticated());
return new UsernamePasswordAuthenticationToken("admin", "123465");
}
// 出现500错误,通过一个存放的map解决两次传递的问题,和密码为null的问题
public Authentication myAuthenticate(Authentication authentication) throws AuthenticationException {
// 简化上面的操作
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
String loginName = token.getName();// 或者 token.getPrincipal();获取用户名
String pwd = myTockenMap.get(loginName);
String loginPwd=null;
if(pwd==null) {
loginPwd=token.getCredentials().toString();
System.out.println("登陆用户名:" + loginName);
System.out.println("登录用户密码" + loginPwd);
// 执行验证
if ("admin".equals(loginName) && "123456".equals(loginPwd)) {
// 设置登录后的令牌
myTockenMap.put(loginName, loginPwd);
// 表示必须创建新的令牌,并设置状态
UsernamePasswordAuthenticationToken checkToken = new UsernamePasswordAuthenticationToken(loginName,
loginPwd);// 當前的authenticated从创建就被设置为true
/*
* token.setAuthenticated(true);//设置当前的令牌已校验 return token;
*/// 这里返回token会发生nullPointException 异常,
// 或者Once created you cannot set this token to authenticated. Create a new
// instance using the constructor which takes a GrantedAuthority list will mark
// this as authenticated.原因:父类中的setAuthenticated中如果设置为true其中就抛出了异常
return checkToken;
} else {
// 返回不为null,表示允许通过
return null;// 如果返回null表示验证没有通过
}
}else {
return new UsernamePasswordAuthenticationToken(loginName,
pwd);
}
}
}
12.测试自定义的安全配置和自定义的令牌
发现问题:
使用systemAuthenticate方法的时候有如下信息产生
-
第一次登录
: 用户名:admin
UsernamePasswordAuthenticationToken@fa794fcc
: Principal: admin; Credentials:
[PROTECTED]; Authenticated:false
; Details:(没有输入密码的登录) -
第二次登录
: org.springframework.security.web.authentication.WebAuthenticationDetails@ffff4c9c
:
RemoteIpAddress: 0:0:0:0:0:0:0:1;SessionId: 7F46F8B5B254A8457365155BB4B597F9
; Not granted any authorities 密碼:123456
isAuthenticated():false
(直接输入正确的用户名和密码) -
第二次登录校验
: 用户名:admin
UsernamePasswordAuthenticationToken@5860350
: Principal: admin; Credentials:
[PROTECTED]; Authenticated: false; Details: null; Not granted any authorities
密碼:null isAuthenticated():false
-
刷新
:用户名:admin
UsernamePasswordAuthenticationToken@5860350
: Principal: admin; Credentials:
[PROTECTED]; Authenticated: false; Details: null; Not granted any authorities
密碼:null isAuthenticated():false
(发现对象地址同样为5860350,表示二次验证的时候令牌应该存储到security的作用域中了
)
总结:
1.发现每次的isAuthenticated()都为false
并且其他时候每次访问页面都会执行令牌校验
2.也就是说只有第二次的校验操作是将当前已完成校验的令牌执行添加操作
,并传递给客户端,这个数据才会在session中存储,并且只会存储用户名和主机,和添加Cookie
3.并且当前的只有验证的第一次的密码是可以检查的,所有的isAuthenticated()都为false
,如果设置为true就会报错
4.就算登录后当前的isAuthenticated()为false
,并且密码为null
5.当前的只要通过验证,就会创建对象,当前的setAuthenticated不能设置为true
,原因父类抛出了异常
6.必须创建一个东西存放当前的用户名和密码用来通过第二次校验
(第二次校验的时候是不会有密码的,只有用户名
)
7.使用当前的security自定的时候踩了一些坑,比如setAuthenticated为true,结果直接报异常,发现父辈中如果将setAuthenticated赋值为true,就直接抛出异常,还有authentication
的实际类型
就是UsernamePasswordAuthenticationToken
类型,可以直接转换,令牌通过后会有第二次校验,此时没有密码
8.当前的配置内存的用户令牌与自定义的令牌同时存在的时候,内存的用户令牌无效,当前自定义令牌有效
,注意自定义的用户令牌具有扩展性,可以使用数据源,或者其他的方式(建议使用自定义令牌
)
以上纯属个人见解,如有问题请指出