以下是一個簡單的示例,展示如何使用AuthenticationProvider自定義身份驗證。首先,創建一個繼承自標準AuthenticationProvider的類,并實現authenticate方法。
import com.kamier.security.web.service.MyUser;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;import java.util.HashMap;
import java.util.Map;public class MobilecodeAuthenticationProvider implements AuthenticationProvider {private UserDetailsService userDetailsService;@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {MobilecodeAuthenticationToken mobilecodeAuthenticationToken = (MobilecodeAuthenticationToken) authentication;String phone = mobilecodeAuthenticationToken.getPhone();String mobileCode = mobilecodeAuthenticationToken.getMobileCode();System.out.println("登陸手機號:" + phone);System.out.println("手機驗證碼:" + mobileCode);// 模擬從redis中讀取手機號對應的驗證碼及其用戶名Map dataFromRedis = new HashMap();dataFromRedis.put("code", "6789");dataFromRedis.put("username", "admin");// 判斷驗證碼是否一致if (!mobileCode.equals(dataFromRedis.get("code"))) {throw new BadCredentialsException("驗證碼錯誤");}// 如果驗證碼一致,從數據庫中讀取該手機號對應的用戶信息MyUser loadedUser = (MyUser) userDetailsService.loadUserByUsername(dataFromRedis.get("username"));if (loadedUser == null) {throw new UsernameNotFoundException("用戶不存在");} else {MobilecodeAuthenticationToken result = new MobilecodeAuthenticationToken(loadedUser, null, loadedUser.getAuthorities());return result;}}@Overridepublic boolean supports(Class> aClass) {return MobilecodeAuthenticationToken.class.isAssignableFrom(aClass);}public void setUserDetailsService(UserDetailsService userDetailsService) {this.userDetailsService = userDetailsService;}
}
?注意這里的supports方法,是實現多種認證方式的關鍵,認證管理器AuthenticationManager會通過這個supports方法來判定當前需要使用哪一種認證方式
。
上面的就是只支持MobilecodeAuthenticationToken
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;/*** 手機驗證碼認證信息,在UsernamePasswordAuthenticationToken的基礎上添加屬性 手機號、驗證碼*/
public class MobilecodeAuthenticationToken extends AbstractAuthenticationToken {private static final long serialVersionUID = 530L;private Object principal;private Object credentials;private String phone;private String mobileCode;public MobilecodeAuthenticationToken(String phone, String mobileCode) {super(null);this.phone = phone;this.mobileCode = mobileCode;this.setAuthenticated(false);}public MobilecodeAuthenticationToken(Object principal, Object credentials, Collection extends GrantedAuthority> authorities) {super(authorities);this.principal = principal;this.credentials = credentials;super.setAuthenticated(true);}public Object getCredentials() {return this.credentials;}public Object getPrincipal() {return this.principal;}public String getPhone() {return phone;}public String getMobileCode() {return mobileCode;}public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {if (isAuthenticated) {throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");} else {super.setAuthenticated(false);}}public void eraseCredentials() {super.eraseCredentials();this.credentials = null;}
}
用戶名密碼
針對用戶名密碼方式,我們可以直接使用自帶的DaoAuthenticationProvider以及對應的UsernamePasswordAuthenticationToken。
實現UserDetailService
UserDetailService服務用以返回當前登錄用戶的用戶信息,可以每一種認證方式實現對應的UserDetailService,也可以使用同一個。這里我們使用同一個UserDetailService服務。(在provider中指定調用哪一個UserDetailService)
最后
在配置器中我們去實例化一個認證管理器AuthenticationManager,這個認證管理器中包含了兩個認證器,分別是MobilecodeAuthenticationProvider(手機驗證碼)、DaoAuthenticationProvider(用戶名密碼)。
下面的只是名字不同改成自己的provider名字即可