SpringSecurity实现自定义登录接口
1、配置类 ConfigClazz(SpringSecuriey的)
@Resource
private DIYUsernamePasswordAuthenticationFilter diyUsernamePasswordAuthenticationFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(
authorize -> authorize
.requestMatchers("/user/**","/").hasRole("user")
.requestMatchers("/manager/**").hasRole("manager")
.requestMatchers("/login/**").permitAll()
.anyRequest()
.authenticated()
);
http.formLogin(AbstractHttpConfigurer::disable);
http.logout(logout ->{
logout
.logoutUrl("/goOut").permitAll()
.logoutSuccessHandler((HttpServletRequest request, HttpServletResponse response, Authentication authentication)->{
Map<String, String[]> parameterMap = request.getParameterMap();
if(!parameterMap.isEmpty() && parameterMap.get("TowLogin")[0].equals("true")){
String json = JSON.toJSONString(Code.NOTowLogin);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
} else {
String json = JSON.toJSONString(Code.SuccessLogout);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
}
});
});
http.addFilterAfter(diyUsernamePasswordAuthenticationFilter, LogoutFilter.class);
http.exceptionHandling(exception ->{
exception.authenticationEntryPoint((HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)->{
String json = JSON.toJSONString(Code.NoLogin);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
});
exception.accessDeniedHandler((HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)->{
String json = JSON.toJSONString(Code.Forbidden);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
});
});
http.sessionManagement(session -> {
session
.maximumSessions(1)
.expiredSessionStrategy((SessionInformationExpiredEvent event)->{
String json = JSON.toJSONString(Code.ForeignLogin);
HttpServletResponse response = event.getResponse();
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
});
});
http.cors(withDefaults());
http.csrf(AbstractHttpConfigurer::disable);
return http.build();
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder(10);
}
- 解释 _scrf 在哪看,只有最初有,后面就没有,但是如果不携带,就不让你访问接口,因此建议禁用
2、DIYUsernamePasswordAuthenticationFilter
- 该类用于替换 UsernamePasswordAuthenticationFilter 过滤器,应用自己自定义的过滤器
@Component
public class DIYUsernamePasswordAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Map<String, String[]> parameterMap = request.getParameterMap();
SUser user = null;
HttpSession session = request.getSession();
if(parameterMap.get("token") != null)
user = (SUser)session.getAttribute(parameterMap.get("token")[0]);
if (user == null) {
filterChain.doFilter(request, response);
return;
}
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(user,user.getPassword(),user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request, response);
}
}
3、DIYAuthenticationProvider
@Component
public class DIYAuthenticationProvider implements AuthenticationProvider {
@Resource
private UserDetailsService userDetailsService;
@Resource
private PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (!passwordEncoder.matches(password, userDetails.getPassword())) {
throw new BadCredentialsException("用户名或密码错误");
}
UsernamePasswordAuthenticationToken authenticatedToken =
new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
authenticatedToken.setDetails(authentication.getDetails());
return authenticatedToken;
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}
4、DIYAuthenticationManager
@Component
public class DIYAuthenticationManager implements AuthenticationManager {
@Resource
AuthenticationProvider authenticationProvider;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
return authenticationProvider.authenticate(authentication);
}
}
5、MySQLUserDetailsManager
@Component
public class MySQLUserDetailsManager implements UserDetailsService{
@Resource
SUserMapper sUserMapper;
@Resource
HttpServletRequest request;
@Override
public UserDetails loadUserByUsername(String account) throws UsernameNotFoundException {
List<SUser> sUsers = sUserMapper.selectAllByEmail(account);
if(sUsers != null && !sUsers.isEmpty()) {
SUser sUser = sUsers.get(0);
HttpSession session = request.getSession();
session.setAttribute(String.valueOf(sUser.getEmail()),sUser);
return sUser;
} else {
throw new UsernameNotFoundException(account);
}
}
}
6、控制层
@Controller
@Tag(name = "登录注册")
@RequestMapping("/login")
public class LoginController {
@Resource
private SUserService sUserService;
@Resource
private AuthenticationManager authenticationManager;
@GetMapping("/getLoginHTML")
public String getLoginHtml(HttpSession session){
boolean aNew = session.isNew();
if(aNew)
return "login";
return "redirect:/goOut?TowLogin=true";
}
@PostMapping("/ooo")
@ResponseBody
public Code login(String account,String password){
SUser sUser = new SUser();
sUser.setEmail(account);
sUser.setPassword(password);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(sUser,password);
Authentication authenticate = authenticationManager.authenticate(authenticationToken);
if(Objects.isNull(authenticate))
throw new AuthenticationCredentialsNotFoundException("用户账号或密码错误");
else{
Map<String, String> map = new HashMap<>();
map.put("token",authenticate.getName());
return new Code<>(Code.OK, map);
}
}
7、增强用户的实体类
- 这里由于要封装用户的详细信息,而用 MybatisX 生成的 User 实体类不能满足需求,因此要实现一个接口
@TableName(value ="s_user")
@Data
@Repository
public class SUser implements Serializable , UserDetails{
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
private Integer age;
private String sex;
private String email;
private String password;
private Integer isForbidden;
private String role;
@TableLogic
private Integer isDelete;
@Serial
@TableField(exist = false)
private static final long serialVersionUID = 1L;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> list = new ArrayList<>();
list.add(new SimpleGrantedAuthority("ROLE_" + role));
return list;
}
@Override
public String getUsername() {
return this.email;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return isForbidden == 1;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
7、依赖
- java版本 17
- springBoot版本 3.2.0
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.4.1</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.37</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
</dependencies>