SpringBoot中使用SpringSecuriy(自定义AuthenticationManager和自定义WebSecurityConfigurerAdapter)

环境: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.当前的配置内存的用户令牌与自定义的令牌同时存在的时候,内存的用户令牌无效,当前自定义令牌有效,注意自定义的用户令牌具有扩展性,可以使用数据源,或者其他的方式(建议使用自定义令牌)

以上纯属个人见解,如有问题请指出

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值