SpringSecurity介绍
SpringSecurity是基于spring的一种声明式的安全框架,充分利用Spring的IOC,DI,与AOP功能,为应用系统提供声明式的安全访问控制功能。它包括了认证和授权,可以在web请求与调用方法时进行身份的认证和授权。
优势:
对身份验证和授权的全面性和可扩展性
防止攻击,如会话固定、跨站请求伪造、点击劫持…
Servlet API集成
与Spring Web MVC的可选集成…
它的最大的优势就在于高度的扩展性。
入门案例
入门
- 导入依赖
<dependencies>
<!-- web起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- springBoot整合Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
</dependencies>
- 编写启动类
package com.ssy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MySecurityApplication {
public static void main(String[] args) {
SpringApplication.run(MySecurityApplication.class,args);
}
}
- 编写controller层
ackage com.ssy.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/login")
public class MySecurityController {
@GetMapping("/hello")
public String hello(){
return "hello security";
}
@GetMapping("/say")
public String say(){
return "say security";
}
@GetMapping("/register")
public String register(){
return "register security";
}
}
这样的话,一个简单的Spring Security就搞定了,接下来就交给测试
上图中的8080为端口号,那么在浏览器地址栏中输入 localhost:8080/login/hello,是访问不到的,而是直接跳转到到http://localhost:8080/login 这个登陆页面,
这是因为在pom文件中加入了
<!-- springBoot整合Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
此时输入用户名user以及密码,那么密码是啥呢?在哪里呢?那我们可以看向idea的控制台
Using generated security password: a384f26f-74f9-4d57-9a68-caa6745c3bd2
是不是就是说明密码(password)是: a384f26f-74f9-4d57-9a68-caa6745c3bd2
然后我们现在就拥有了用户名与密码,输入
并点击Sign in,那么我们可以看到
在当前页面下,地址栏中的地址发生了改变
并在当前页面输出 hello security
这个输出的hello security就是我们在controller层中的MySecurityController类中的hello方法中的返回值
在上述的案例中,用户名与密码是由框架默认生成的,那么我们也可以指定用户名与密码
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
/*
* 认证配置
*
* */
@Configuration
//指示一个类声明一个或多个@Bean方法,并且可以由Spring容器处理,以便在运行时为这些bean生成BeanDefinition和服务请求
@EnableWebSecurity
//开启web认证
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
/*
*构建了认证的信息
*
* */
@Bean//将我们创建的对象放入spring容器中
//配置UserDetailsService,用来指定用户访问
public UserDetailsService userDetailsService(){
//利用InMemoryUserDetailsManager在我内存中构建用户详情
//InMemoryUserDetailsManager实现了UserDetailsManager接口,UserDetailsManager接口继承了UserDetailsService
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
//用户详情对象 用户、(明文)密码、角色
inMemoryUserDetailsManager.createUser(User.withUsername("张轩杰").password("{noop}1234").authorities("P1","Role_ADMIE").build());
inMemoryUserDetailsManager.createUser(User.withUsername("于晓彤").password("{noop}1234").authorities("O1","ROLE_SELLER").build());
return inMemoryUserDetailsManager;
}
}
运行时可以在控制中看出并没有显示密码
D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\bin\java.exe -XX:TieredStopAtLevel=1 -noverify -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true -javaagent:D:\Java\Idea\Idea2018.3\lib\idea_rt.jar=14514:D:\Java\Idea\Idea2018.3\bin -Dfile.encoding=UTF-8 -classpath D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\charsets.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\deploy.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\ext\access-bridge-64.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\ext\cldrdata.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\ext\dnsns.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\ext\jaccess.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\ext\jfxrt.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\ext\localedata.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\ext\nashorn.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\ext\sunec.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\ext\sunjce_provider.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\ext\sunmscapi.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\ext\sunpkcs11.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\ext\zipfs.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\javaws.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\jce.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\jfr.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\jfxswt.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\jsse.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\management-agent.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\plugin.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\resources.jar;D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\jre\lib\rt.jar;D:\File\WorkSpace\First\hello-world\spring-security\target\classes;D:\File\Repository\repository\org\springframework\boot\spring-boot-starter-web\2.3.4.RELEASE\spring-boot-starter-web-2.3.4.RELEASE.jar;D:\File\Repository\repository\org\springframework\boot\spring-boot-starter\2.3.4.RELEASE\spring-boot-starter-2.3.4.RELEASE.jar;D:\File\Repository\repository\org\springframework\boot\spring-boot-starter-logging\2.3.4.RELEASE\spring-boot-starter-logging-2.3.4.RELEASE.jar;D:\File\Repository\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;D:\File\Repository\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;D:\File\Repository\repository\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;D:\File\Repository\repository\org\apache\logging\log4j\log4j-to-slf4j\2.13.3\log4j-to-slf4j-2.13.3.jar;D:\File\Repository\repository\org\apache\logging\log4j\log4j-api\2.13.3\log4j-api-2.13.3.jar;D:\File\Repository\repository\org\slf4j\jul-to-slf4j\1.7.30\jul-to-slf4j-1.7.30.jar;D:\File\Repository\repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\File\Repository\repository\org\springframework\spring-core\5.2.9.RELEASE\spring-core-5.2.9.RELEASE.jar;D:\File\Repository\repository\org\springframework\spring-jcl\5.2.9.RELEASE\spring-jcl-5.2.9.RELEASE.jar;D:\File\Repository\repository\org\yaml\snakeyaml\1.26\snakeyaml-1.26.jar;D:\File\Repository\repository\org\springframework\boot\spring-boot-starter-json\2.3.4.RELEASE\spring-boot-starter-json-2.3.4.RELEASE.jar;D:\File\Repository\repository\com\fasterxml\jackson\core\jackson-databind\2.11.2\jackson-databind-2.11.2.jar;D:\File\Repository\repository\com\fasterxml\jackson\core\jackson-annotations\2.11.2\jackson-annotations-2.11.2.jar;D:\File\Repository\repository\com\fasterxml\jackson\core\jackson-core\2.11.2\jackson-core-2.11.2.jar;D:\File\Repository\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.11.2\jackson-datatype-jdk8-2.11.2.jar;D:\File\Repository\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.11.2\jackson-datatype-jsr310-2.11.2.jar;D:\File\Repository\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.11.2\jackson-module-parameter-names-2.11.2.jar;D:\File\Repository\repository\org\springframework\boot\spring-boot-starter-tomcat\2.3.4.RELEASE\spring-boot-starter-tomcat-2.3.4.RELEASE.jar;D:\File\Repository\repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.38\tomcat-embed-core-9.0.38.jar;D:\File\Repository\repository\org\glassfish\jakarta.el\3.0.3\jakarta.el-3.0.3.jar;D:\File\Repository\repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.38\tomcat-embed-websocket-9.0.38.jar;D:\File\Repository\repository\org\springframework\spring-web\5.2.9.RELEASE\spring-web-5.2.9.RELEASE.jar;D:\File\Repository\repository\org\springframework\spring-beans\5.2.9.RELEASE\spring-beans-5.2.9.RELEASE.jar;D:\File\Repository\repository\org\springframework\spring-webmvc\5.2.9.RELEASE\spring-webmvc-5.2.9.RELEASE.jar;D:\File\Repository\repository\org\springframework\spring-context\5.2.9.RELEASE\spring-context-5.2.9.RELEASE.jar;D:\File\Repository\repository\org\springframework\spring-expression\5.2.9.RELEASE\spring-expression-5.2.9.RELEASE.jar;D:\File\Repository\repository\org\springframework\boot\spring-boot-starter-security\2.3.4.RELEASE\spring-boot-starter-security-2.3.4.RELEASE.jar;D:\File\Repository\repository\org\springframework\spring-aop\5.2.9.RELEASE\spring-aop-5.2.9.RELEASE.jar;D:\File\Repository\repository\org\springframework\security\spring-security-config\5.3.4.RELEASE\spring-security-config-5.3.4.RELEASE.jar;D:\File\Repository\repository\org\springframework\security\spring-security-core\5.3.4.RELEASE\spring-security-core-5.3.4.RELEASE.jar;D:\File\Repository\repository\org\springframework\security\spring-security-web\5.3.4.RELEASE\spring-security-web-5.3.4.RELEASE.jar;D:\File\Repository\repository\org\projectlombok\lombok\1.18.12\lombok-1.18.12.jar;D:\File\Repository\repository\org\springframework\boot\spring-boot-autoconfigure\2.2.2.RELEASE\spring-boot-autoconfigure-2.2.2.RELEASE.jar;D:\File\Repository\repository\org\springframework\boot\spring-boot\2.3.4.RELEASE\spring-boot-2.3.4.RELEASE.jar com.ssy.MySecurityApplication
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.4.RELEASE)
2021-08-21 17:18:19.679 INFO 2948 --- [ main] com.ssy.MySecurityApplication : Starting MySecurityApplication on W10-20210820635 with PID 2948 (D:\File\WorkSpace\First\hello-world\spring-security\target\classes started by ssy in D:\File\WorkSpace\First\hello-world)
2021-08-21 17:18:19.683 INFO 2948 --- [ main] com.ssy.MySecurityApplication : No active profile set, falling back to default profiles: default
2021-08-21 17:18:20.303 INFO 2948 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2021-08-21 17:18:20.308 INFO 2948 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-08-21 17:18:20.308 INFO 2948 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.38]
2021-08-21 17:18:20.351 INFO 2948 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-08-21 17:18:20.351 INFO 2948 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 638 ms
2021-08-21 17:18:20.501 INFO 2948 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@55b62629, org.springframework.security.web.context.SecurityContextPersistenceFilter@30f4b1a6, org.springframework.security.web.header.HeaderWriterFilter@66f66866, org.springframework.security.web.csrf.CsrfFilter@3b0f7d9d, org.springframework.security.web.authentication.logout.LogoutFilter@17f460bb, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@cda4919, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@4c7a078, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@a53bb6f, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@757f675c, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@3e1162e7, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5298dead, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5e63cad, org.springframework.security.web.session.SessionManagementFilter@4d666b41, org.springframework.security.web.access.ExceptionTranslationFilter@28f8e165, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@4fbb001b]
2021-08-21 17:18:20.559 INFO 2948 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2021-08-21 17:18:20.657 INFO 2948 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-08-21 17:18:20.664 INFO 2948 --- [ main] com.ssy.MySecurityApplication : Started MySecurityApplication in 1.282 seconds (JVM running for 1.789)
还是在浏览器地址栏输入http://localhost:8080/login/hello,还是直接跳转到http://localhost:8080/login 这个登陆页面,
我们只需要输入我们自己定义的用户名以及密码
单机Sign in之后会看到
,那么就说明我们的这个配置已完成,那么上面的代码是什么意思呢?
将UserDetailsService 放入到spring容器中,Spring Security会利用容器来获取用户的信息。用户的信息详情被暂时放在了InMemoryUserDetailsManager 实现类中,并分别创建了两个用户以及各自的密码、权限
登录成功后即可访问到全部资源
接下来进行授权的操作
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()//允许表单登录 /longin
.and()
.logout()//退出登录 /logout
.and()
.csrf().disable()//关闭跨站请求伪造 框架中国默认实现了跨站请求伪造
.authorizeRequests() //开始配置授权信息
.antMatchers("/register").permitAll() //不登录即可访问 开放访问 /register
.antMatchers("/hello").hasAuthority("P1") //具有P1权限才可以访问
.antMatchers("/say").hasRole("SELLER") //具有SELLER 角色才可以访问 hasRole判断角色,角色名称前缀必须是ROLE_,必须大写 默认省略仅限hasRole
.anyRequest().authenticated(); //其他的登录之后就可以访问
}
代码编写完毕后,测试运行,在浏览器输入http://localhost:8080/register 可以直接得到
在http://localhost:8080/register加上logout可以得到
在地址栏输入http://localhost:8080/hello 会得到
然后输入用户名及密码
单机Sign in会得到
在地址栏输入http://localhost:8080/say会得到
该用户不具备指定的角色权限
- 扩展
CSRF(Cross-site request forgery)跨站请求伪造,也被称为"One Click Attack"或者 Session Riding,通常缩写为 CSRF 或者 XSRF,是一种对网站的恶意利用。
像上面的案例,随着用户越来越多时,配置文件也会越来越多,麻烦也不是特别麻烦,也只不过是加点语句的问题,主要是容易引起冲突问题。所以引入了注解,利用注解控制权限
==注解方式控制权限
package com.ssy.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PreDestroy;
@RestController
public class MySecurityController {
@PreAuthorize("hasAuthority('p1')")
@GetMapping("/hello")
public String hello(){
return "hello security";
}
@PreAuthorize("hasRole('SELLER')")
@GetMapping("/say")
public String say(){
return "say security";
}
@GetMapping("/register")
public String register(){
return "register security";
}
}
package com.ssy.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
/*
* 认证配置
*
* */
@Configuration
//指示一个类声明一个或多个@Bean方法,并且可以由Spring容器处理,以便在运行时为这些bean生成BeanDefinition和服务请求
@EnableWebSecurity
//开启web认证
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启注解控制权限
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
/*
*构建了认证的信息
*
* */
@Bean//将我们创建的对象放入spring容器中
//配置UserDetailsService,用来指定用户访问
public UserDetailsService userDetailsService(){
//利用InMemoryUserDetailsManager在我内存中构建用户详情
//InMemoryUserDetailsManager实现了UserDetailsManager接口,UserDetailsManager接口继承了UserDetailsService
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
//用户详情对象 用户、(明文)密码、角色
inMemoryUserDetailsManager.createUser(User.withUsername("张轩杰").password("{noop}1234").authorities("P1","ROLE_ADMIN").build());
inMemoryUserDetailsManager.createUser(User.withUsername("于晓彤").password("{noop}1234").authorities("O1","ROLE_SELLER").build());
return inMemoryUserDetailsManager;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()//允许表单登录 /longin
.and()
.logout()//退出登录 /logout
.and()
.csrf().disable()//关闭跨站请求伪造 框架中国默认实现了跨站请求伪造
.authorizeRequests() //开始配置授权信息
.antMatchers("/register").permitAll() //不登录即可访问 开放访问 /register
/* .antMatchers("/hello").hasAuthority("P1") //具有P1权限才可以访问
.antMatchers("/say").hasRole("SELLER") //具有SELLER 角色才可以访问 hasRole判断角色,角色名称前缀必须是ROLE_,必须大写 默认省略仅限hasRole*/
.anyRequest().authenticated(); //其他的登录之后就可以访问
}
}
在使用@PreAuthorize注解时,需要开启全局方法授权开关,加上注解@EnableGlobalMethodSecurity(prePostEnabled=true)
经过演示,我们对于Spring Security框架有了一定的了解,但也有两个问题:
- 1.密码采用的是明文方式,不安全
- 2.用户名、密码直接通过程序硬编码,无法动态获取,不够灵活
密码加密
我们在这里对密码进行加密处理,保证一定的安全性
可逆加密算法
可逆加密算法就是在加密后, 密文可以反向解密得到密码原文;
1).对称加密
优点: 对称加密算法的优点是算法公开、计算量小、加密速度快、加密效率高。
缺点: 没有非对称加密安全。
常见的对称加密算法:DES、3DES、DESX、Blowfish、RC4、RC5、RC6和AES
2). 非对称加密
指加密和解密使用不同密钥的加密算法,也称为公私钥加密。假设两个用户要加密交换数据,双方交换公钥,使用时一方用对方的公钥加密,另一方即可用自己的私钥解密
私钥加密,持有私钥或公钥才可以解密
公钥加密,持有私钥才可解密
优点: 非对称加密与对称加密相比,其安全性更好;
缺点: 非对称加密的缺点是加密和解密花费时间长、速度慢,只适合对少量数据进行加密。
不可逆加密算法
一旦加密就不能反向解密得到密码原文 。通常用于密码数据加密。
常见的不可逆加密算法有: MD5 、SHA、HMAC
MD5与Bcrypt
MD5
比较常见的加密方式,通过MD5生成的密文,在大数据的背景下,是可以被破译的
可以在用户注册时,限制用户输入密码的长度及复杂度,从而增加破解难度
Bcrypt【加盐加密】
BCrypt 算法将 salt (盐值、随机匹配)随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理 salt 问题。
package com.ssy.test;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class MyPassWorld {
public static void main(String[] args) {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
for (int i = 0; i < 10; i++) {
System.out.println(bCryptPasswordEncoder.encode("1234"));
}
}
}
得到结果:
$2a$10$TW5Cqkbu9qDR7fx/RG4aD.pGQ92qZJrC4o6gCbAOFiMQcs7E9hLCe
$2a$10$R8RLvpqrFdtcbyfm1Sb.UO.o940OshYEDaa6VjQQWXGVd6ijYuKvi
$2a$10$8nAcHAIV92cCffEThoKuPexvfQlBKIEjmtO4emjuImyxFBIV/HH6G
$2a$10$SMH6HzGX4rFkUKv5qMU73ucbHi4HZj9WPAaEhYLy.sXcIWy5DOUlK
$2a$10$eHxvEeaWB0GTdT63egeYAu/SN44KPamv8WAyusPVFuamxr8E8dLFa
$2a$10$3Yqkm.7YsXK20NmyxRvHF.xrpup4C.A9YV.YFs7wy0Dyhz0rpRVK6
$2a$10$eN4CH2Z9h56QsPFZDU0hGemdKbaiKsX23VTZzrF0D5e6CXac2IAAC
$2a$10$sDw39sN9vvkPIYIURdzXc.nfB4EN7yO387ZsDJbdx8JuEKO6xsYd6
$2a$10$O3/YDMINhU4fYgWnXQAGIuRr1FumfB7bTo4DeUVUauDXu7dLup5JG
$2a$10$efcRbvTDhYxAOeptJUlEkOn8eAolXlmXTf4SDbxnbx4GMdzzRtska
验证密码
boolean matches = bCryptPasswordEncoder.matches("1234", "$2a$10$efcRbvTDhYxAOeptJUlEkOn8eAolXlmXTf4SDbxnbx4GMdzzRtska");
if (matches){
System.out.println("密码相同");
}else {
System.out.println("密码不相同");
}
}//返回值为true, 则代表验证通过; 反之, 验证不通过
得到
D:\Java\ActualRuntime\JDK\JDK\jdk_1.8\bin\java.exe 5.2.9.RELEASE.jar;D:\File\Repository\repository\org\springframework\spring-tx\5.2.9.RELEASE\spring-tx-5.2.9.RELEASE.jar com.ssy.test.MyPassWorld
密码相同
Process finished with exit code 0
将程序进行优化
动态获取数据库中的数据,将密码进行加密
yml文件
server:
port: 8080
spring:
application:
name: spring-security
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/security_demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 1234
mybatis-plus:
type-aliases-package: com.ssy.pojo
configuration:
map-underscore-to-camel-case: true
启动类
@SpringBootApplication
@MapperScan(basePackages = {"com.ssy.mapper"})//包扫描
public class MySecurityApplication {
public static void main(String[] args) {
SpringApplication.run(MySecurityApplication.class,args);
}
}
pojo实体类
@Data
@TableName(value = "tb_user") //指定数据表
public class User {
@TableId(type = IdType.AUTO)//自增
private Integer id;
private String username;
private String password;
private String roles;
}
controller层
package com.ssy.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MySecurityController {
@PreAuthorize("hasAuthority('p1')")
@GetMapping("/hello")
public String hello(){
return "hello security";
}
@PreAuthorize("hasRole('SELLER')")
@GetMapping("/say")
public String say(){
return "say security";
}
@GetMapping("/register")
public String register(){
return "register security";
}
}
mapper层
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
config层
package com.ssy.config;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ssy.mapper.UserMapper;
import com.ssy.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
@Component //加载到spring容器
public class UserConfig implements UserDetailsService {
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getUsername,name);
User user = userMapper.selectOne(lambdaQueryWrapper);
System.out.println(user);
return new org.springframework.security.core.userdetails.User(
name,
user.getPassword(),
AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRoles()));
}
}
package com.ssy.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
/*
* 认证配置
*
* */
@Configuration
//指示一个类声明一个或多个@Bean方法,并且可以由Spring容器处理,以便在运行时为这些bean生成BeanDefinition和服务请求
@EnableWebSecurity
//开启web认证
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启注解控制权限
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Bean //将BCryptPasswordEncoder放入spring容器中
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
/*
//配置密码加密器 加盐加密
@Bean
BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
*//*
*构建了认证的信息
*
* *//*
@Bean//将我们创建的对象放入spring容器中
//配置UserDetailsService,用来指定用户访问
public UserDetailsService userDetailsService(){
//利用InMemoryUserDetailsManager在我内存中构建用户详情
//InMemoryUserDetailsManager实现了UserDetailsManager接口,UserDetailsManager接口继承了UserDetailsService
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
//用户详情对象 用户、(明文)密码、角色
inMemoryUserDetailsManager.createUser(User.withUsername("张轩杰").password("$2a$10$Lev2qXRsApjBO9pj8tEp9e8qeT.dx80P4gTUaXwTRQLcTk7.07Nm.").authorities("P1","ROLE_ADMIN").build());
inMemoryUserDetailsManager.createUser(User.withUsername("于晓彤").password("$2a$10$Lev2qXRsApjBO9pj8tEp9e8qeT.dx80P4gTUaXwTRQLcTk7.07Nm.").authorities("O1","ROLE_SELLER").build());
return inMemoryUserDetailsManager;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()//允许表单登录 /longin
.and()
.logout()//退出登录 /logout
.and()
.csrf().disable()//关闭跨站请求伪造 框架中国默认实现了跨站请求伪造
.authorizeRequests() //开始配置授权信息
.antMatchers("/register").permitAll() //不登录即可访问 开放访问 /register
*//* .antMatchers("/hello").hasAuthority("P1") //具有P1权限才可以访问
.antMatchers("/say").hasRole("SELLER") //具有SELLER 角色才可以访问 hasRole判断角色,角色名称前缀必须是ROLE_,必须大写 默认省略仅限hasRole*//*
.anyRequest().authenticated(); //其他的登录之后就可以访问
}*/
}