SpringSecurity 认证
基本概念
认证
用户认证(Authentication) 是系统认为用户是否能登录
授权
用户授权(Authorization)系统判断用户是否有权限去做某些事情
Web组件
不管是单体结构还是分布式、微服务项目,只要还是Web项目,都会遵循一个主干线:
发起请求>接受请求>处理请求>响应请求
- filter/interceptor:定位拦截/过滤请求
- servlet/controller:定位接收请求、处理请求、响应请求
- listener/applicationListener:监听器
案例分析
快速入门
创建 maven 工程 引入依赖
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.3.12.RELEASE</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
创建启动类
package com.yy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
创建controller
package com.yy.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello(){
return "hello";
}
}
接口访问
引入 security
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
访问接口
认证
认证定制-用户定制
- 基于内存
- 基于数据库
基于内存
@Configuration
public class SpringSecurityConfig {
@Bean
public UserDetailsService userDetailsService(){
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("pengyy").password("123456").authorities("p1").build());
manager.createUser(User.withUsername("xiaoming").password("123456").authorities("p2").build());
return manager;
}
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
}
认证定制-页面定制
继承WebSecurityConfigurerAdapter,重写configure接口 protected void configure(HttpSecurity http) throws Exception
/**
* 自定义配置
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 禁用csrf保护
http.csrf().disable();
// url 路径权限控制
http.authorizeRequests()
// 匹配 /login /login.html permitAll -- 全部放行
.antMatchers("/login.html").permitAll()
.antMatchers("/login").permitAll()
// anyRequest 匹配所有请求 authenticated 认证拦截
.anyRequest().authenticated();
// 用户登录控制
http.formLogin()
// 指定登录请求
.loginProcessingUrl("/login")
// 指定登录页面
.loginPage("/login.html");
}
<!-- 在 resources 目录下创建 static 文件夹,在static文件夹下创建 login.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="post">
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
认证定制-登录路径定制
loginProcessingUrl(“/user/login”)
/**
* 自定义配置
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 禁用csrf保护
http.csrf().disable();
// url 路径权限控制
http.authorizeRequests()
// 匹配 /login /login.html permitAll -- 全部放行
.antMatchers("/login.html").permitAll()
.antMatchers("/user/login").permitAll()
// anyRequest 匹配所有请求 authenticated 认证拦截
.anyRequest().authenticated();
// 用户登录控制
http.formLogin()
// 指定登录请求
.loginProcessingUrl("/user/login")
// 指定登录页面
.loginPage("/login.html");
}
认证定制-登录参数定制
usernameParameter(“uname”)
passwordParameter(“passwd”)
/**
* 自定义配置
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 禁用csrf保护
http.csrf().disable();
// url 路径权限控制
http.authorizeRequests()
// 匹配 /login /login.html permitAll -- 全部放行
.antMatchers("/login.html").permitAll()
.antMatchers("/user/login").permitAll()
// anyRequest 匹配所有请求 authenticated 认证拦截
.anyRequest().authenticated();
// 用户登录控制
http.formLogin()
// 指定登录请求
.loginProcessingUrl("/user/login")
// 指定登录页面
.loginPage("/login.html")
.usernameParameter("uname")
.passwordParameter("passwd");
}
认证定制-登录成功跳转路径定制
SpringSecurity登录成功后,默认跳转到上一个路径,如果需要定制跳转路径,可以使用下面配置
successForwardUrl(“/success”)
// 用户登录控制
http.formLogin()
// 指定登录请求
.loginProcessingUrl("/user/login")
// 指定登录页面
.loginPage("/login.html")
.usernameParameter("uname")
.passwordParameter("passwd")
.successForwardUrl("/success");
认证定制-登录失败跳转路径定制
failureForwardUrl(“/fail”) 登录失败,定制的跳转路径需要放通
antMatchers(“/fail”).permitAll()
/**
* 自定义配置
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 禁用csrf保护
http.csrf().disable();
// url 路径权限控制
http.authorizeRequests()
// 匹配 /login /login.html permitAll -- 全部放行
.antMatchers("/login.html").permitAll()
.antMatchers("/user/login").permitAll()
.antMatchers("/fail").permitAll()
// anyRequest 匹配所有请求 authenticated 认证拦截
.anyRequest().authenticated();
// 用户登录控制
http.formLogin()
// 指定登录请求
.loginProcessingUrl("/user/login")
// 指定登录页面
.loginPage("/login.html")
.usernameParameter("uname")
.passwordParameter("passwd")
.successForwardUrl("/success")
.failureForwardUrl("/fail");
}
认证定制-登录成功逻辑定制
successHandler(new MyAuthenticationSuccessHandler())
// 登录成功之后处理器
.successHandler(new MyAuthenticationSuccessHandler())
// 登录成功跳转
.successForwardUrl(“/success”)
如果登陆成功之后处理器和登陆成功跳转两个都设置了,那么往后的会覆盖前面的,以上 successHandler 登录成功处理器在 successForwardUrl 登录成功跳转的上面,那么最终是以 successForwardUrl 为主
package com.yy.handler;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 登录成功之后处理器
*/
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
// 自定义登录成功之后逻辑
// 传统项目
// response.sendRedirect("/success");
// 前后端分离项目--json
response.setContentType("application/json;charset=utf-8");
String data = "{\"code\":200,\"msg\": \"登录成功\",\"data\": null}";
response.getWriter().write(data);
}
}
// 用户登录控制
http.formLogin()
// 指定登录请求
.loginProcessingUrl("/user/login")
// 指定登录页面
.loginPage("/login.html")
// 修改登录用户名、密码参数
.usernameParameter("uname")
.passwordParameter("passwd")
// 登录成功之后处理器
.successHandler(new MyAuthenticationSuccessHandler())
// 登录成功跳转
.successForwardUrl("/success")
// 登录失败跳转
.failureForwardUrl("/fail");
认证定制-登录失败逻辑定制
failureHandler(new MyAuthenticationFailureHandler())
package com.yy.handler;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
// 自定义登录失败之后逻辑
// 传统项目
// response.sendRedirect("/success");
// 前后端分离项目--json
response.setContentType("application/json;charset=utf-8");
String data = "{\"code\":200,\"msg\": \"登录失败\",\"data\": "+exception.getMessage()+"}";
response.getWriter().write(data);
}
}
// 用户登录控制
http.formLogin()
// 指定登录请求
.loginProcessingUrl("/user/login")
// 指定登录页面
.loginPage("/login.html")
// 修改登录用户名、密码参数
.usernameParameter("uname")
.passwordParameter("passwd")
// 登录成功跳转
// .successForwardUrl("/success")
// 登录成功之后处理器
.successHandler(new MyAuthenticationSuccessHandler())
// 登录失败跳转
// .failureForwardUrl("/fail")
// 登录失败之后处理器
.failureHandler(new MyAuthenticationFailureHandler());
认证定制-登出控制
默认 /logout ,定制的话使用
http.logout()
.logoutUrl(“/user/logout”);
认证定制-登出成功跳转路径定制
logoutSuccessUrl(“/logout-success”) 登出成功跳转路径需要放开
antMatchers(“/logout-success”).permitAll()
/**
* 自定义配置
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 禁用csrf保护
http.csrf().disable();
// url 路径权限控制
http.authorizeRequests()
// 匹配 /login /login.html permitAll -- 全部放行
.antMatchers("/login.html").permitAll()
.antMatchers("/user/login").permitAll()
.antMatchers("/fail").permitAll()
.antMatchers("/logout-success").permitAll()
// anyRequest 匹配所有请求 authenticated 认证拦截
.anyRequest().authenticated();
// 用户登录控制
http.formLogin()
// 指定登录请求
.loginProcessingUrl("/user/login")
// 指定登录页面
.loginPage("/login.html")
// 修改登录用户名、密码参数
.usernameParameter("uname")
.passwordParameter("passwd")
// 登录成功跳转
// .successForwardUrl("/success")
// 登录成功之后处理器
.successHandler(new MyAuthenticationSuccessHandler())
// 登录失败跳转
// .failureForwardUrl("/fail")
// 登录失败之后处理器
.failureHandler(new MyAuthenticationFailureHandler());
http.logout()
.logoutUrl("/user/logout")
.logoutSuccessUrl("/logout-success");
}
认证定制-登出成功逻辑定制
logoutSuccessHandler(new MyLogoutSuccessHandler());
package com.yy.handler;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
// 自定义登出成功之后逻辑
// 传统项目
// response.sendRedirect("/success");
// 前后端分离项目--json
response.setContentType("application/json;charset=utf-8");
String data = "{\"code\":200,\"msg\": \"登出成功\",\"data\": null}";
response.getWriter().write(data);
}
}
http.logout()
.logoutUrl("/user/logout")
// .logoutSuccessUrl("/logout-success")
.logoutSuccessHandler(new MyLogoutSuccessHandler());
认证完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/user/login" method="post">
用户名:<input type="text" name="uname"/><br/>
密码:<input type="password" name="passwd"/><br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
package com.yy.config;
import com.yy.handler.MyAuthenticationFailureHandler;
import com.yy.handler.MyAuthenticationSuccessHandler;
import com.yy.handler.MyLogoutSuccessHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public UserDetailsService userDetailsService(){
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("pengyy").password("123456").authorities("p1").build());
manager.createUser(User.withUsername("xiaoming").password("123456").authorities("p2").build());
return manager;
}
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
/**
* 自定义配置
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 禁用csrf保护
http.csrf().disable();
// url 路径权限控制
http.authorizeRequests()
// 匹配 /login /login.html permitAll -- 全部放行
.antMatchers("/login.html").permitAll()
.antMatchers("/user/login").permitAll()
.antMatchers("/fail").permitAll()
.antMatchers("/logout-success").permitAll()
// anyRequest 匹配所有请求 authenticated 认证拦截
.anyRequest().authenticated();
// 用户登录控制
http.formLogin()
// 指定登录请求
.loginProcessingUrl("/user/login")
// 指定登录页面
.loginPage("/login.html")
// 修改登录用户名、密码参数
.usernameParameter("uname")
.passwordParameter("passwd")
// 登录成功跳转
// .successForwardUrl("/success")
// 登录成功之后处理器
.successHandler(new MyAuthenticationSuccessHandler())
// 登录失败跳转
// .failureForwardUrl("/fail")
// 登录失败之后处理器
.failureHandler(new MyAuthenticationFailureHandler());
http.logout()
.logoutUrl("/user/logout")
// .logoutSuccessUrl("/logout-success")
.logoutSuccessHandler(new MyLogoutSuccessHandler());
}
}
package com.yy.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello(){
return "hello";
}
@RequestMapping("/success")
public String success(){
return "success";
}
@RequestMapping("/fail")
public String fail(){
return "fail";
}
@RequestMapping("/logout-success")
public String logoutSuccess(){
return "logout-success";
}
}
package com.yy.handler;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 登录成功之后处理器
*/
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
// 自定义登录成功之后逻辑
// 传统项目
// response.sendRedirect("/success");
// 前后端分离项目--json
response.setContentType("application/json;charset=utf-8");
String data = "{\"code\":200,\"msg\": \"登录成功\",\"data\": null}";
response.getWriter().write(data);
}
}
package com.yy.handler;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
// 自定义登录失败之后逻辑
// 传统项目
// response.sendRedirect("/success");
// 前后端分离项目--json
response.setContentType("application/json;charset=utf-8");
String data = "{\"code\":200,\"msg\": \"登录失败\",\"data\": "+exception.getMessage()+"}";
response.getWriter().write(data);
}
}
package com.yy.handler;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
// 自定义登出成功之后逻辑
// 传统项目
// response.sendRedirect("/success");
// 前后端分离项目--json
response.setContentType("application/json;charset=utf-8");
String data = "{\"code\":200,\"msg\": \"登出成功\",\"data\": null}";
response.getWriter().write(data);
}
}