1. Create a springBoot project (the bottom layer of spring6.0, JDK17)
1. Add dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
After the configuration is completed and the access controller is started, the login page will appear.
The default user user password console uuid output, you can see the trace source code
2 Use the configured username and password;
security:
user:
name: admin
password: 123456
Use your own configured login and then you can access the control layer API
3. Log out
http://127.0.0.1:8080/logout
2. Memory-based multi-user management
1. Create user details interface configuration class
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
//自定义配置类实现用户详情
@Configuration
public class MyUserSecurityConfig {
@Bean
public UserDetailsService userDetailsService() {
UserDetails user1 = User.builder().username("admin").password("123456").build();
UserDetails user2 = User.builder().username("test").password("123456").build();
InMemoryUserDetailsManager memoryUserDetailsManager = new InMemoryUserDetailsManager();
memoryUserDetailsManager.createUser(user1);
memoryUserDetailsManager.createUser(user2);
return memoryUserDetailsManager;
}
}
Access interface login test test error: The following
must be encrypted
Modify the configuration class
package com.example.db.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
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 MyUserSecurityConfig {
@Bean
public UserDetailsService userDetailsService() {
UserDetails user1 = User.builder().username("admin").password("123456")
.roles("admin").build();
UserDetails user2 = User.builder().username("test").password("123456")
.roles("stu").build();
InMemoryUserDetailsManager memoryUserDetailsManager = new InMemoryUserDetailsManager();
memoryUserDetailsManager.createUser(user1);
memoryUserDetailsManager.createUser(user2);
return memoryUserDetailsManager;
}
//配置密码加密器
//没有加密NoOpPasswordEncoder
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
Through the above configuration, you can log in normally and access the API interface. The user in the configuration file is invalid.
3. Password encryption
1. CSDN password leakage incident, database plain text storage
1. The front-end password is encrypted to the back-end, using the same password encryption rules. The front-end encryption is compared with the database password verification rules.
Common encryption algorithm MD5 sha RSA
2. Use the implementation class provided by the PasswordEncoder interface
Salt interference factor
BCryptPasswordEncoder encrypts and verifies the original password
BCryptPasswordEncoder cryptPasswordEncoder=new BCryptPasswordEncoder();
String encode = cryptPasswordEncoder.encode("123456");
String encode2 = cryptPasswordEncoder.encode("123456");
String encode3 = cryptPasswordEncoder.encode("123456");
log.info(encode);
log.info(encode2);
log.info(encode3);
boolean pwd1 = cryptPasswordEncoder.matches("123456", encode);
boolean pwd2 = cryptPasswordEncoder.matches("123456", encode2);
boolean pwd3 = cryptPasswordEncoder.matches("123456", encode3);
log.info("pwd1="+pwd1);
log.info("pwd2="+pwd2);
log.info("pwd3="+pwd3);
Encrypt the above civilized password
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
//自定义类实现用户详情接口
@Configuration
public class MyUserSecurityConfig {
@Bean
public UserDetailsService userDetailsService() {
UserDetails user1 = User.builder().username("admin").password(passwordEncoder().encode("123456"))
.roles("admin").build();
UserDetails user2 = User.builder().username("test").password(passwordEncoder().encode("654321"))
.roles("stu").build();
InMemoryUserDetailsManager memoryUserDetailsManager = new InMemoryUserDetailsManager();
memoryUserDetailsManager.createUser(user1);
memoryUserDetailsManager.createUser(user2);
return memoryUserDetailsManager;
}
//配置密码加密器
//没有加密NoOpPasswordEncoder
@Bean
public PasswordEncoder passwordEncoder() {
// return NoOpPasswordEncoder.getInstance();
return new BCryptPasswordEncoder();
}
}
在这里插入代码片
Return login information test
Add controller
package com.example.db.control;
import org.springframework.boot.autoconfigure.neo4j.Neo4jProperties;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/loginUser")
public Authentication loginUser(Authentication authentication) {
return authentication;
}
@GetMapping("/loginUser2")
public Principal loginUser2(Principal principal) {
return principal;
}
@GetMapping("/loginUser3")
public Principal loginUser3() {
//通过安全上下文持有器
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication;
}
}
The access interface returns the logged in user information as follows:
Configure permissions
UserDetails user1 = User.builder().username("admin").password(passwordEncoder().encode("123456"))
.roles("admin")
.authorities("stu:del") //配置权限
{
"authorities": [{
"authority": "stu:del"
}],
"details": {
"remoteAddress": "127.0.0.1",
"sessionId": "026ED34E46FC9022C29B200090F14924"
},
"authenticated": true,
"principal": {
"password": null,
"username": "admin",
"authorities": [{
"authority": "stu:del"
}],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
},
"credentials": null,
"name": "admin"
}
4. Method-based authorization
开启@EnableGlobalMethodSecurity(prePostEnabled = true)
package com.example.db.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
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.WebSecurityCustomizer;
import org.springframework.security.web.SecurityFilterChain;
import static org.springframework.security.config.Customizer.withDefaults;
//从 Spring Security 5.7.0-M2开始 WebSecurityConfigurerAdapter 被标记为过期,鼓励用户转向基于组件的 security 配置
@Configuration
@Slf4j
//全局方法授权
@EnableWebSecurity // 启用SpringSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry
.anyRequest().authenticated());
http.formLogin().permitAll();
return http.build();
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().requestMatchers("/test/**");
}
}
Controller configuration
package com.example.db.control;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/student")
@RestController
public class StudentController {
@GetMapping("/stu")
@PreAuthorize("hasAuthority('stu')")
public String test() {
return "I am a student";
}
}
5. The processing results are returned to JSON, which is easy to process on the front end.
Response message VO
package com.example.db.pojo;
import lombok.Data;
/**
* 自定义响应结构
*/
@Data
public class Result {
// 响应业务状态
private Integer code;
// 响应消息
private String message;
// 响应中的数据
private Object data;
public Result() {
}
public Result(Object data) {
this.code = 200;
this.message = "OK";
this.data = data;
}
public Result(String message, Object data) {
this.code = 200;
this.message = message;
this.data = data;
}
public Result(Integer code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
}
public static Result ok() {
return new Result(null);
}
public static Result ok(String message) {
return new Result(message, null);
}
public static Result ok(Object data) {
return new Result(data);
}
public static Result ok(String message, Object data) {
return new Result(message, data);
}
public static Result build(Integer code, String message) {
return new Result(code, message, null);
}
public static Result build(Integer code, String message, Object data) {
return new Result(code, message, data);
}
}
1. Authentication successfully processed
package com.example.db.config;
import com.example.db.pojo.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
//认证成功处理器
@Component
@Slf4j
public class AuthorSuccesssHandler implements AuthenticationSuccessHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Result result=Result.build(1,"登录成功");
String responsejson=objectMapper.writeValueAsString(result);
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(responsejson);
response.getWriter().flush();
}
}
2.Authentication failure handler
package com.example.db.config;
import com.example.db.pojo.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
//认证失败处理器
@Component
public class AuthorFailHandler implements AuthenticationFailureHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
Result result=Result.build(-1,"登陆失败");
String responsejson=objectMapper.writeValueAsString(result);
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(responsejson);
response.getWriter().flush();
}
}
3. Exit the login handler
package com.example.db.config;
import com.example.db.pojo.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
//注销处理器
@Component
@Slf4j
public class AppLoginOutHandler implements LogoutSuccessHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Result result = Result.build(1, "退出登录成功");
String responsejson = objectMapper.writeValueAsString(result);
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(responsejson);
response.getWriter().flush();
}
}
4. Resource access denial handler
package com.example.db.config;
import com.example.db.pojo.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
//资源访问拒绝处理器
@Slf4j
@Component
public class AppAcessDeiedHandler implements AccessDeniedHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
Result result = Result.build(0, "你没有权限访问资源");
String responsejson = objectMapper.writeValueAsString(result);
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(responsejson);
response.getWriter().flush();
}
}
5. Configure various processors
package com.example.db.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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.WebSecurityCustomizer;
import org.springframework.security.web.SecurityFilterChain;
//从 Spring Security 5.7.0-M2开始 WebSecurityConfigurerAdapter 被标记为过期,鼓励用户转向基于组件的 security 配置
@Configuration
@Slf4j
//全局方法授权
@EnableWebSecurity // 启用SpringSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
//认证成功处理器
@Autowired
private AuthorSuccesssHandler authorSuccesssHandler;
@Autowired
//认证失败处理器
private AuthorFailHandler authorFailHandler;
//退出登录处理器
@Autowired
private AppLoginOutHandler appLoginOutHandler;
//访问拒绝处理器
@Autowired
private AppAcessDeiedHandler appAcessDeiedHandler;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry
.anyRequest().authenticated());
http.formLogin()
.successHandler(authorSuccesssHandler)//认证成功
.failureHandler(authorFailHandler)//认证失败
.permitAll();
http.logout().logoutSuccessHandler(appLoginOutHandler); //退出登录
http.exceptionHandling().accessDeniedHandler(appAcessDeiedHandler);//访问资源失败
return http.build();
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().requestMatchers("/test/**");
}
}