参考教程:陈木鑫老师的《Spring Security 实战》html
经过Intellij IDEA建立Spring Boot项目的方式有许多种,其中最简单的方式就是使用Spring Initializr
工具。
Spring Initializr 容许咱们提早选定一些经常使用的项目依赖,此处咱们选择 Security 做为构建Spring
Security项目的最小依赖,选择Web做为Spring Boot构建Web应用的核心依赖。
Next :
Next:
建立项目的目录结构:前端
在自动构建的Spring Security 项目中,Spring Initializr 为咱们引入了如下依赖:java
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
咱们点开spring-boot-starter-security
能够看到,其包含了如下依赖:web
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.3.1.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <scope>compile</scope> </dependency>
其中 spring-security-web
和spring-security-config
两个核心模块,正是官方建议引入的Spring Security最小依赖。spring
在项目中声明一个测试路由TestController
:后端
package com.haan.springsecuritydemo; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @GetMapping public String hello(){ return "Hello Spring Security!"; } }
SpringsecuritydemoApplication
运行SpringsecuritydemoApplication
,默认启动 8080
端口,打开浏览器,访问localhost:8080
,咱们发现页面跳转到了localhost:8080/login
:
在引入Spring Security项目以后,虽然没有进行任何相关的配置或编码,但Spring Security有一个默认的运行状态,要求在通过表单基本认证后才能访问对应的URL资源,其默认使用的用户名为 user
,密码则是动态生成并打印到控制台的一串随机码。翻看控制台的打印信息:
输入用户名和密码后,单击“登陆”按钮便可成功访问:浏览器
基本表单认证中,用户名和密码都是能够配置的,最多见的就是在resources下的application
配置文件中修改:安全
spring.security.user.name=user02 spring.security.user.password=aaaaaa
从新启动程序,发现控制台再也不打印默认密码串了,此时使用咱们自定义的用户名和密码便可登陆。服务器
protected void configure(HttpSecurity http) throws Exception { this.logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity)."); ((HttpSecurity)((HttpSecurity)((AuthorizedUrl)http.authorizeRequests().anyRequest()).authenticated().and()).formLogin().and()).httpBasic(); }
能够看到 WebSecurityConfigurerAdapter
已经默认声明了一些安全特性:app
spring boot 默认定义了DefaultConfigurerAdapter
,由@ConditionalOnMissingBean
可知当没有其余WebSecurityConfigurerAdapter
被定义时,将使用DefaultConfigurerAdapter
:
@Configuration( proxyBeanMethods = false ) @ConditionalOnClass({WebSecurityConfigurerAdapter.class}) @ConditionalOnMissingBean({WebSecurityConfigurerAdapter.class}) @ConditionalOnWebApplication( type = Type.SERVLET ) public class SpringBootWebSecurityConfiguration { public SpringBootWebSecurityConfiguration() { } @Configuration( proxyBeanMethods = false ) @Order(2147483642) static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter { DefaultConfigurerAdapter() { } } }
Spring boot提供了WebSecurityConfigurerAdapter
的默认实现DefaultConfigurerAdapter
,可以提供基本表单登陆认证。
虽然自动生成的表单登陆页能够方便、快速地启动,可是大多数应用程序更但愿提供本身的表单登陆页,此时就须要咱们提供本身的WebSecurityConfigurerAdapter
来代替DefaultConfigurerAdapter
,覆写WebSecurityConfigurerAdapter
的configure
方法:
@EnableWebSecurity public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .loginPage("/myLogin.html") //指明登陆页面 .permitAll() //指明登陆页容许全部进行访问 .and() .csrf().disable(); } }
authorizeRequests()
方法实际上返回了一个 URL 拦截注册器,咱们能够调用它提供的anyRequest()
、antMatchers()
和 regexMatchers()
等方法来匹配系统的URL,并为其指定安全策略。formLogin()
方法和httpBasic()
方法都声明了须要Spring Security提供的表单认证方式,分别返回对应的配置器。其中formLogin().loginPage("/myLogin.html")
指定自定义的登陆页 /myLogin.html
,同时,Spring Security会用/myLogin.html
注册一个POST
路由,用于接收登陆请求。
csrf()
方法是Spring Security提供的跨站请求伪造防御功能,当咱们继承WebSecurityConfigurer Adapter
时会默认开启 csrf()
方法。访问localhost:8080
,咱们发现,页面就跳转到了localhost:8080/myLogin.html
,因为咱们静态文件中并无myLogin.html
文件,因此提示了一个404的white page
:
咱们在resources/static
文件夹下建立页面myLogin.html
:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Hello Security!</title> </head> <body> <h3>登陆页</h3> <form action="/myLogin.html" method="post"> <input type="text" name="username"> <br/> <input type="text" name="password"> <br/> <input type="submit" value="login"> </form> </body> </html>
重启服务,再次访问localhost:8080
:
输入上面的application.properties
中配置的用户登陆帐户和密码进行登陆,登录成功:
在自定义表单登陆页以后,处理登陆请求的URL
也会相应改变,默认状况下,
若是只配置loginPage
而不配置loginProcessingUrl
的话那么loginProcessingUrl
默认就是loginPage
,若是须要自定义登陆请求的URL
,须要配置loginProcessingUrl
:
重启登陆,咱们发现中间访问了localhost:8080/myLogin
补充:
loginPage
和loginProcessingUrl
- 二者都不配置:默认都是
/login
- 二者都配置:按本身的来
- 只配置
loginProcessingUrl
:loginPage
默认/login
- 只配置
loginPage
:loginProcessingUrl
默认就是loginPage
此时,有些读者可能会有疑问,由于按照惯例,在发送登陆请求并认证成功以后,页面会跳转回原访问页。在某些系统中的确是跳转回原访问页的,但在部分先后端彻底分离、仅靠JSON完成全部交互的系统中,通常会在登陆时返回一段 JSON 数据,告知前端成功登陆成功与否,由前端决定如何处
理后续逻辑,而非由服务器主动执行页面跳转。这在Spring Security中一样能够实现。
表单登陆配置模块提供了 successHandler()
和 failureHandler()
两个方法,分别处理登陆成功和登陆失败的逻辑。
successHandler()
方法带有一个Authentication
参数,携带当前登陆用户名及其角色等信息;failureHandler()
方法携带一个AuthenticationException
异常参数。具体处理方式需按照系统的状况自定义。@EnableWebSecurity public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .loginPage("/myLogin.html") //指明登陆页面 .loginProcessingUrl("/myLogin") //指明处理登录的URL路径,即登录表单提交请求 .successHandler(new AuthenticationSuccessHandler() { // 设置登陆成功的处理器 @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { PrintWriter responseWriter = httpServletResponse.getWriter(); String name = authentication.getName(); responseWriter.write(name+" login success!"); } }) .failureHandler(new AuthenticationFailureHandler() { // 设置登陆失败的处理器 @Override public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { PrintWriter responseWriter = httpServletResponse.getWriter(); responseWriter.write("login error!"); } }) .permitAll() //指明登陆页容许全部进行访问 .and() .csrf().disable(); } }
正确的帐号密码:
错误的帐号密码: