日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

spring boot security自定義認證

作者:不識君的荒漠 更新時間: 2023-07-04 編程語言

前言

前置閱讀

spring boot security快速使用示例
spring boot security之前后端分離配置

說明

實際場景,我們一般是把用戶信息保存在db中(也可能是調用三方接口),需要自定義用戶信息加載或認證部分的邏輯,下面提供一個示例。

代碼示例

定義用戶bean

@AllArgsConstructor
@Data
public class User {

    private String username;

    private String password;
}

定義Mapper

示例,代碼寫死了,并不是實際從數據庫或某個存儲查詢用戶信息:

@Component
public class UserMapper {

   public User select(String username) {
        return new User(username, "pass");
    }
}

定義加載用戶數據的類

UserDetailsService 是spring security內置的加載用戶信息的接口,我們只需要實現這個接口:

@Slf4j
@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    public static final UserDetails INVALID_USER =
            new org.springframework.security.core.userdetails.User("invalid_user", "invalid_password", Collections.emptyList());

    private final UserMapper userMapper;

    public UserDetailsServiceImpl(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 根據用戶名從數據庫查詢用戶信息
        User user = userMapper.select(username);
        if (user == null) {
            /**
             * 如果沒查詢到這個用戶,考慮兩種選擇:
             * 1. 返回一個標記無效用戶的常量對象
             * 2. 返回一個不可能認證通過的用戶
             */
            return INVALID_USER;
//            return new User(username, System.currentTimeMillis() + UUID.randomUUID().toString(), Collections.emptyList());
        }
        /**
         * 這里返回的用戶密碼是否為庫里保存的密碼,是明文/密文,取決于認證時密碼比對部分的實現,每個人的場景不一樣,
         * 因為使用的是不加密的PasswordEncoder,所以可以返回明文
         */
        return new org.springframework.security.core.userdetails.User(username, user.getPassword(), Collections.emptyList());
    }
}

自定義認證的bean配置

@Configuration
public class WebConfiguration {

    @Bean
    public PasswordEncoder passwordEncoder() {
        // 示例,不對密碼進行加密處理
        return NoOpPasswordEncoder.getInstance();
    }


    @Bean
    public AuthenticationManager authenticationManager(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        // 設置加載用戶信息的類
        provider.setUserDetailsService(userDetailsService);
        // 比較用戶密碼的時候,密碼加密方式
        provider.setPasswordEncoder(passwordEncoder);
        return new ProviderManager(Arrays.asList(provider));
    }

}

注意,因為這個是示例,AuthenticationProvider使用的是spring security的DaoAuthenticationProvider ,在實際場景中,如果不滿足可以自定義實現或者繼承DaoAuthenticationProvider ,重寫其中的:additionalAuthenticationChecks方法,主要就是認證檢查的,默認實現如下:

	@Override
	@SuppressWarnings("deprecation")
	protected void additionalAuthenticationChecks(UserDetails userDetails,
			UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
		if (authentication.getCredentials() == null) {
			this.logger.debug("Failed to authenticate since no credentials provided");
			throw new BadCredentialsException(this.messages
					.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
		}
		String presentedPassword = authentication.getCredentials().toString();
		// 就是比對下請求傳過來的密碼和根據該用戶查詢的密碼是否一致,passwordEncoder是根據不同的加密算法進行加密,示例我們用的是NoOpPasswordEncoder,也就是原始明文比對
		if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
			this.logger.debug("Failed to authenticate since password does not match stored value");
			throw new BadCredentialsException(this.messages
					.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
		}
	}

定義登錄接口

@RequestMapping("/login")
@RestController
public class LoginController {

    private final AuthenticationManager authenticationManager;

    public LoginController(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @PostMapping()
    public Object login(@RequestBody User user) {
        try {
            // 使用定義的AuthenticationManager進行認證處理
            Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));
            // 認證通過,設置到當前上下文,如果當前認證過程后續還有處理的邏輯需要的話。這個示例是沒有必要了
            SecurityContextHolder.getContext().setAuthentication(authenticate);
            return "login success";
        }catch (Exception e) {
            return "login failed";
        }
    }

    /**
     * 獲取驗證碼,需要的話,可以提供一個驗證碼獲取的接口,在上面的login里把驗證碼傳進來進行比對
     */
    @GetMapping("/captcha")
    public Object captcha() {
        return "1234";
    }
}

自定義HttpSecurity

@Component
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 在這里自定義配置
        http.authorizeRequests()
                // 登錄相關接口都允許訪問
                .antMatchers("/login/**").permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .exceptionHandling()
                // 認證失敗返回401狀態碼,前端頁面可以根據401狀態碼跳轉到登錄頁面
                .authenticationEntryPoint((request, response, authException) ->
                        response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()))
                .and().cors()
                // csrf是否決定禁用,請自行考量
                .and().csrf().disable()
                // 采用http 的基本認證.
                .httpBasic();
    }
}

測試

示例中,用戶密碼寫死是:pass,
用一個錯誤的密碼試一下,響應登錄失敗:
在這里插入圖片描述
使用正確的密碼,響應登錄成功:
在這里插入圖片描述

原文鏈接:https://blog.csdn.net/x763795151/article/details/131499404

  • 上一篇:沒有了
  • 下一篇:沒有了
欄目分類
最近更新