关于springSecurity6的登录认证架构如图:
在该登录认证架构中主要是通过Authentication的对象进行存储账户密码后,通过AuthenticationManager进行认证用户,在认证通过后就登录成功,其中在AuthenticationManager认证用户名密码主要是通过DaoAuthenticationProvider完成,其中的认证过程为下图
故该文章通过采用DaoAuthenticationProvider来进行自定义登录架构
其中需要在SpringSecurity配置类中暴露DaoAuthenticationProvider的bean,在暴露DaoAuthenticationProvider的bean时候,需要设置他的UserDetailsService和PasswordEncoder,因为DaoAuthenticationProvider主要是通过设置的这两个对象进行认证,其中UserDetailsServices 来加载真实的用户信息,PasswordWEncoder则是获取密码的加密方式
package com.infinityai.config;
import com.infinityai.domain.UserServerManager;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
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.web.DefaultSecurityFilterChain;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.NullSecurityContextRepository;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@EnableMethodSecurity
@EnableWebSecurity
@Configuration
public class SecurityConfig {
@Resource
private LoginSuccessHandle loginSuccessHandle;//登录成功处理
@Resource
private LoginFailHandle loginFailHandle;//登录错误处理
@Resource
private DaoAuthenticationEntryPoint authenticationEntryPoint;//认证错误处理
@Resource
private DaoDeniedHandler daoDeniedHandler;//权限错误处理
@Resource
private JwtDaoAuthentication jwtDaoAuthentication;//jwt认证处理
@Autowired
private UserServerManager userServerManager;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(c->c.disable())
.cors(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults())
.authorizeHttpRequests(r->
r.requestMatchers("/druid/**","/file/**","/admin/login").permitAll()
.anyRequest().authenticated())
.formLogin(f->
f.failureHandler(loginFailHandle)
.successHandler(loginSuccessHandle)
)
.addFilterBefore(jwtDaoAuthentication, UsernamePasswordAuthenticationFilter.class)
.requestCache(r->r.disable())//请求缓存关闭,认证成功后跳到的请求,前后端分离不需要这个
.securityContext(s->s.securityContextRepository(new NullSecurityContextRepository()))//前后端无需会话管理
.sessionManagement(s->s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))//前后端无需会话管理
;
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(daoDeniedHandler);
return http.build();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOriginPattern("*");//修改为添加而不是设置,* 最好改为实际的需要,我这是非生产配置,所以粗暴了一点
configuration.addAllowedMethod("*");//修改为添加而不是设置
configuration.addAllowedHeader("*");//这里很重要,起码需要允许 Access-Control-Allow-Origin
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider(){
// 自定义用户登录的 认证架构 需要设置userDetailsService和passwordencoder
DaoAuthenticationProvider provider=new LoginAuthenticationProvider();
provider.setUserDetailsService(userServerManager);
provider.setPasswordEncoder(new BCryptPasswordEncoder());
return provider;
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
在配置完DaoAuthenticationProvider后就可以直接自定义登录接口了
package com.infinityai.controller;
import com.alibaba.fastjson2.JSON;
import com.infinityai.domain.UserServer;
import com.infinityai.domain.UserServerManager;
import com.infinityai.entity.User;
import com.infinityai.mapper.impl.UserServiceImpl;
import com.infinityai.tool.JwtUtils;
import com.infinityai.tool.RedisUtils;
import com.infinityai.tool.Result;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
@RestController
public class BasicController {
@Autowired
DaoAuthenticationProvider authenticationProvider; //自动注入
@Autowired
private RedisUtils redisUtils;
@PostMapping("/admin/login")
public Result adminLogin(@RequestBody User user) {
System.out.println(user);
if (user.getUsername().isEmpty() || user.getPassword().isEmpty()) {
return Result.error("账户密码为空");
} else {
System.out.println("认证前的信息获取");
Authentication authentication = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());//需要认证的信息
Authentication authenticate = authenticationProvider.authenticate(authentication); //进行认证
if (authenticate.isAuthenticated()) {
// 用户登录成功
UserServer userServer = (UserServer) authenticate.getPrincipal();
redisUtils.set(authenticate.getName(), userServer);
return Result.success(JwtUtils.createToken(authenticate.getName()));
} else {
return Result.error("账户密码错误");
}
}
}
}
这样子就完成了简易的SpringSecurity