1.创建springBoot 项目工程(spring6.0的底层、JDK17)
1.添加依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>
配置完成启动访问controller会出现登录页面
默认用户user 密码 控制台uuid输出,跟踪源码可以看到
2 使用配置的用户名密码;
security:user:name: adminpassword: 123456
使用自己配置的登录然后就可以访问控制层api
3.退出登录
http://127.0.0.1:8080/logout
2.基于内存的多用户管理
1.创建用户详情接口配置类
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 {@Beanpublic 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;}}
访问接口登录 test test 报错:如下
必须加密
修改配置类
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 {@Beanpublic 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@Beanpublic PasswordEncoder passwordEncoder() {return NoOpPasswordEncoder.getInstance();}}
通过上面配置可以正常登录,访问api接口,配置文件中的用户失效
3.密码加密
1.CSDN密码泄露事件,数据库明文存储
1.前端密码加密给后端,使用相同的密码加密规则,前端加密对比数据库密码校验规则。
常见加密算法MD5 sha RSA
2、使用PasswordEncoder接口提供的实现类
加盐干扰因子
BCryptPasswordEncoder 加密和校验原始密码
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);
加密上面文明密码
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 {@Beanpublic 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@Beanpublic PasswordEncoder passwordEncoder() {
// return NoOpPasswordEncoder.getInstance();return new BCryptPasswordEncoder();}}在这里插入代码片
返回登录信息测试
添加控制器
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;}
}
访问接口返回登录用户的信息如下
配置权限
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.基于方法的授权
开启@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 {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry.anyRequest().authenticated());http.formLogin().permitAll();return http.build();}@Beanpublic WebSecurityCustomizer webSecurityCustomizer() {return (web) -> web.ignoring().requestMatchers("/test/**");}}
控制器配置
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.处理结果返回JSON 前端好处理
响应报文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.认证成功处理
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 {@Autowiredprivate ObjectMapper objectMapper;@Overridepublic 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.认证失败处理器
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;
//认证失败处理器
@Componentpublic class AuthorFailHandler implements AuthenticationFailureHandler {@Autowiredprivate ObjectMapper objectMapper;@Overridepublic 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.退出登录处理器
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 {@Autowiredprivate ObjectMapper objectMapper;@Overridepublic 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.资源访问拒绝处理器
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 {@Autowiredprivate ObjectMapper objectMapper;@Overridepublic 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.配置各种处理器
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 {//认证成功处理器@Autowiredprivate AuthorSuccesssHandler authorSuccesssHandler;@Autowired//认证失败处理器private AuthorFailHandler authorFailHandler;//退出登录处理器@Autowiredprivate AppLoginOutHandler appLoginOutHandler;//访问拒绝处理器@Autowiredprivate AppAcessDeiedHandler appAcessDeiedHandler;@Beanpublic 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();}@Beanpublic WebSecurityCustomizer webSecurityCustomizer() {return (web) -> web.ignoring().requestMatchers("/test/**");}}