当前位置:   article > 正文

Spring Security (二):自定义登陆_springboot security 自定义登录

springboot security 自定义登录

目录

前言

引入依赖

原理解析

最佳实践

自定义AuthenticationFilter

自定义AuthenticationManager

自定义Provider

自定义UserDetailsService

自定义用户信息

自定义认证成功handler

配置SecurityConfig

项目验证


前言

        Spring Security作为当下最流行的认证授权框架,提供了非常全面且完善的认证授权流程,并且通过与Springboot的结合,极大的简化了开发与使用。spring官方也提供了详细的文档和丰富的应用实例。

官方文档:Spring Security

应用实例:https://github.com/thombergs/code-examples/tree/master/spring-security

        但在我们的实际开发工作中,我们绝大部分都是前后端分离的项目,作为后端开发人员,我们并不关心前段页面的跳转,此外,大多数前后端业务数据都是通过JSON方式传递的,并不是Spring security 默认的form 格式。以下便是一个简单的通过JSON方式自定义登陆认证的项目简介。

引入依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-security</artifactId>
  4. </dependency>

原理解析

        spring security 提供了一个实现了DelegatingFilterProxy的Filter,使Servlet container 的生命周期和 Spring’s ApplicationContext建立联系,从而达到通过Filter对web请求进行授权认证,具体流程如下图所示:

delegatingfilterproxy

securityfilterchain

在spring security中,所有的filter都被放在SecurityFilterChain中 按照顺序被调用处理web request.

具体的源码解析可以参考:Spring Security (一):自定义登陆_见面说Hello的博客-CSDN博客

因此,要想实现自定义登陆认证,需要自定义Filter,具体项目代码如下所示。

最佳实践

自定义AuthenticationFilter

通过JSON方式获取用户名密码

  1. public class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
  2. public CustomUsernamePasswordAuthenticationFilter() {
  3. super();
  4. }
  5. @Override
  6. public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
  7. if (!request.getMethod().equals("POST") || (!request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE))) {
  8. throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
  9. }
  10. // 2. 判断是否是json格式请求类型
  11. if (request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)) {
  12. try {
  13. CustomUser customUser = new ObjectMapper().readValue(request.getInputStream(), CustomUser.class);
  14. String username = customUser.getUsername();
  15. String password = customUser.getPassword();
  16. UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
  17. setDetails(request, authenticationToken);
  18. return this.getAuthenticationManager().authenticate(authenticationToken);
  19. } catch (IOException e) {
  20. throw new AuthenticationServiceException(e.getMessage());
  21. }
  22. }
  23. return super.attemptAuthentication(request, response);
  24. }
  25. }

自定义AuthenticationManager

  1. public class CustomAuthenticationManager implements AuthenticationManager {
  2. public CustomAuthenticationManager() {
  3. super();
  4. }
  5. private DaoAuthenticationProvider authenticationProvider;
  6. public void setAuthenticationProvider(DaoAuthenticationProvider authenticationProvider) {
  7. this.authenticationProvider = authenticationProvider;
  8. }
  9. @Override
  10. public Authentication authenticate(Authentication authentication) throws AuthenticationException {
  11. return authenticationProvider.authenticate(authentication);
  12. }
  13. }

自定义Provider

  1. public class CustomAuthenticationProvider extends DaoAuthenticationProvider {
  2. public CustomAuthenticationProvider() {
  3. super();
  4. }
  5. }

自定义UserDetailsService

这里我为了方便仅构造了一个Map,实际应用中应从数据库获取

  1. @Service
  2. public class CustomUserDetailsService implements UserDetailsService {
  3. protected static final Map<String, CustomUser> map = new HashMap<>();
  4. public static Map<String, CustomUser> getMap() {
  5. return map;
  6. }
  7. static {
  8. String pwd = new BCryptPasswordEncoder().encode("userPwd");
  9. map.put("user", new CustomUser(1L, "user", pwd));
  10. map.put("admin", new CustomUser(2L, "admin", pwd));
  11. }
  12. @Override
  13. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  14. CustomUser customUser = map.get(username);
  15. if (customUser == null) {
  16. throw new UsernameNotFoundException("username " + username + " is not found");
  17. }
  18. return new CustomUserDetails(customUser);
  19. }
  20. }

自定义用户信息

  1. public class CustomUser implements Serializable {
  2. private Long userId;
  3. private String username;
  4. private String password;
  5. public CustomUser() {
  6. }
  7. public CustomUser(Long userId, String username, String password) {
  8. this.userId = userId;
  9. this.username = username;
  10. this.password = password;
  11. }
  12. public Long getUserId() {
  13. return userId;
  14. }
  15. public void setUserId(Long userId) {
  16. this.userId = userId;
  17. }
  18. public String getUsername() {
  19. return username;
  20. }
  21. public void setUsername(String username) {
  22. this.username = username;
  23. }
  24. public String getPassword() {
  25. return password;
  26. }
  27. public void setPassword(String password) {
  28. this.password = password;
  29. }
  30. }

自定义认证成功handler

  1. public class CustomLoginSuccessHandler implements AuthenticationSuccessHandler {
  2. @Override
  3. public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
  4. response.setCharacterEncoding("UTF-8");
  5. response.setContentType("application/json; charset=utf-8");
  6. response.setStatus(HttpServletResponse.SC_OK);
  7. UserDetails customUser = (UserDetails) authentication.getPrincipal();
  8. String username = customUser.getUsername();
  9. response.addCookie(new Cookie("username", username));
  10. WebResponse<String> success = WebResponse.success(username);
  11. response.getWriter().write(JSON.toJSONString(success));
  12. }
  13. }

自定义认证失败Handler

  1. package com.study.security.config;
  2. import com.alibaba.fastjson.JSON;
  3. import org.springframework.security.core.AuthenticationException;
  4. import org.springframework.security.web.authentication.AuthenticationFailureHandler;
  5. import javax.servlet.ServletException;
  6. import javax.servlet.http.Cookie;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import java.io.IOException;
  10. /**
  11. * @author Say Hello
  12. * @version 1.0.0
  13. * @Date 2023/7/27
  14. * @Description
  15. */
  16. public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
  17. @Override
  18. public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
  19. response.setCharacterEncoding("UTF-8");
  20. response.setContentType("application/json; charset=utf-8");
  21. response.setStatus(HttpServletResponse.SC_OK);
  22. WebResponse<Object> fail = WebResponse.fail();
  23. response.getWriter().write(JSON.toJSONString(fail));
  24. }
  25. }

配置SecurityConfig

  1. package com.study.security.config;
  2. import com.study.security.config.cookie.CustomCookieAuthenticationFilter;
  3. import com.study.security.config.phone.CustomPhoneAuthenticationManager;
  4. import com.study.security.config.phone.CustomPhoneAuthenticationProcessingFilter;
  5. import com.study.security.config.phone.CustomPhoneAuthenticationProvider;
  6. import com.study.security.service.CustomPhoneDetailsService;
  7. import com.study.security.service.CustomUserDetailsService;
  8. import org.springframework.beans.factory.annotation.Value;
  9. import org.springframework.context.annotation.Bean;
  10. import org.springframework.context.annotation.Configuration;
  11. import org.springframework.context.annotation.Primary;
  12. import org.springframework.core.annotation.Order;
  13. import org.springframework.security.authentication.AuthenticationManager;
  14. import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
  15. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  16. import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  17. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  18. import org.springframework.security.crypto.password.PasswordEncoder;
  19. import org.springframework.security.web.SecurityFilterChain;
  20. import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
  21. import javax.annotation.Resource;
  22. /**
  23. * @author Say Hello
  24. * @version 1.0.0
  25. * @Date 2023/7/26
  26. * @Description
  27. */
  28. @Configuration
  29. @EnableWebSecurity
  30. public class SecurityConfig {
  31. @Value("${spring.security.custom.url.login}")
  32. String loginUrl;
  33. @Resource
  34. CustomUserDetailsService customUserDetailsService;
  35. @Bean
  36. public PasswordEncoder customPasswordEncoder() {
  37. return new BCryptPasswordEncoder();
  38. }
  39. @Bean
  40. @Order(1)
  41. public SecurityFilterChain customSecurityFilterChain(HttpSecurity http) throws Exception {
  42. http.authorizeRequests()
  43. .anyRequest().authenticated()
  44. .and()
  45. .csrf().disable()
  46. //向SecurityFilterChain中插入自定以的AuthenticationManager
  47. .authenticationManager(customAuthenticationManager())
  48. .authenticationManager(customPhoneAuthenticationManager())
  49. //向SecurityFilterChain中插入自定以的AuthenticationProvider
  50. .authenticationProvider(customAuthenticationProvider())
  51. .authenticationProvider(customPhoneAuthenticationProvider())
  52. .exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint())
  53. .and()
  54. .logout().logoutUrl(logoutUrl)
  55. .logoutSuccessHandler(new CustomLogoutSuccessHandler());
  56. // 将loginFilter过滤器添加到UsernamePasswordAuthenticationFilter过滤器所在的位置
  57. http.addFilterAt(customUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
  58. return http.build();
  59. }
  60. @Bean
  61. public CustomUsernamePasswordAuthenticationFilter customUsernamePasswordAuthenticationFilter() {
  62. CustomUsernamePasswordAuthenticationFilter customUsernamePasswordAuthenticationFilter = new CustomUsernamePasswordAuthenticationFilter();
  63. //配置登陆认证的入口
  64. customUsernamePasswordAuthenticationFilter.setFilterProcessesUrl(loginUrl);
  65. //配置manager
  66. customUsernamePasswordAuthenticationFilter.setAuthenticationManager(customAuthenticationManager());
  67. //设置认证成功Handler
  68. customUsernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(new CustomLoginSuccessHandler());
  69. //设置认证失败Handler
  70. customUsernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler());
  71. return customUsernamePasswordAuthenticationFilter;
  72. }
  73. @Bean
  74. public AuthenticationManager customAuthenticationManager() {
  75. CustomAuthenticationManager customAuthenticationManager = new CustomAuthenticationManager();
  76. customAuthenticationManager.setAuthenticationProvider(customAuthenticationProvider());
  77. return customAuthenticationManager;
  78. }
  79. @Bean
  80. public DaoAuthenticationProvider customAuthenticationProvider() {
  81. CustomAuthenticationProvider customAuthenticationProvider = new CustomAuthenticationProvider();
  82. customAuthenticationProvider.setPasswordEncoder(customPasswordEncoder());
  83. customAuthenticationProvider.setUserDetailsService(customUserDetailsService);
  84. return customAuthenticationProvider;
  85. }
  86. }

项目验证

未登陆访问

认证失败 

认证成功

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/921910
推荐阅读
相关标签
  

闽ICP备14008679号