第一步:新建filter,这里以手机验证码登录为例子
/**
* @Author: 朱维
* @Date 16:52 2019/11/27
* /phoneLogin?telephone=13000000000&smsCode=1000
*/
public class PhoneLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
/**
* 验证码登录请求参数:手机号码
*/
private static final String SPRING_SECURITY_RESTFUL_PHONE_KEY = "telephone";
/**
* 验证码登录请求参数:短信验证码
*/
private static final String SPRING_SECURITY_RESTFUL_VERIFY_CODE_KEY = "smsCode";
/**
* 验证码登录请求参数:登录地址
*/
private static final String SPRING_SECURITY_RESTFUL_LOGIN_URL = "/phone-login";
private boolean postOnly = true;
public PhoneLoginAuthenticationFilter() {
super(new AntPathRequestMatcher(SPRING_SECURITY_RESTFUL_LOGIN_URL, "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
AbstractAuthenticationToken authRequest;
String principal;
String credentials;
// 手机验证码登陆
principal = obtainParameter(request, SPRING_SECURITY_RESTFUL_PHONE_KEY);
credentials = obtainParameter(request, SPRING_SECURITY_RESTFUL_VERIFY_CODE_KEY);
principal = principal.trim();
authRequest = new PhoneAuthenticationToken(principal, credentials);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
private void setDetails(HttpServletRequest request,
AbstractAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
private String obtainParameter(HttpServletRequest request, String parameter) {
String result = request.getParameter(parameter);
return result == null ? "" : result;
}
第二步新建provider
/**
* 手机验证码登录
* @Author: 朱维
* @Date 16:26 2019/11/27
*/
public class PhoneAuthenticationProvider extends MyAbstractUserDetailsAuthenticationProvider {
private UserDetailsService userDetailsService;
@Autowired
private RedisTemplate redisTemplate;
@Override
protected void additionalAuthenticationChecks(UserDetails var1, Authentication authentication) throws AuthenticationException {
if(authentication.getCredentials() == null) {
this.logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(this.messages.getMessage("PhoneAuthenticationProvider.badCredentials", "Bad credentials"));
} else {
String presentedPassword = authentication.getCredentials().toString();
String telephone = authentication.getPrincipal().toString();
this.logger.info("电话号码:"+telephone);
// 验证码验证,调用公共服务查询 key 为authentication.getPrincipal()的value, 并判断其与验证码是否匹配
String key =getSMSCode(telephone);
if(key==null){
this.logger.info("未获取redis存储的key:"+key);
throw new UsernameNotFoundException("验证码不对");
}
Object smsCode = redisTemplate.opsForValue().get(key);
if(smsCode==null){
this.logger.info("redis中未找到验证码");
throw new UsernameNotFoundException("验证码不对");
}
if(!(smsCode.toString()).equals(presentedPassword)){
this.logger.debug("Authentication failed: verifyCode does not match stored value");
throw new UsernameNotFoundException("验证码不对");
}
}
}
/**
* 获取验证码
* @return
*/
private String getSMSCode(String telephone){
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(attributes!=null){
HttpServletRequest request = attributes.getRequest();
String ip = PubUtils.getIpAddress(request);
this.logger.info("获取的IP:"+ip);
String key = Md5Utils.getMD5Uppercase(telephone+ SMSConstant.LOGIN_SMS_TYPE +ip);
return key;
}
return null;
}
@Override
protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
PhoneAuthenticationToken result = new PhoneAuthenticationToken(principal, authentication.getCredentials(), user.getAuthorities());
result.setDetails(authentication.getDetails());
return result;
}
@Override
protected UserDetails retrieveUser(String phone, Authentication authentication) throws AuthenticationException {
UserDetails loadedUser;
try {
loadedUser = this.getUserDetailsService().loadUserByUsername(phone);
} catch (UsernameNotFoundException var6) {
throw var6;
} catch (Exception var7) {
throw new InternalAuthenticationServiceException(var7.getMessage(), var7);
}
if(loadedUser == null) {
throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
} else {
return loadedUser;
}
}
@Override
public boolean supports(Class<?> authentication) {
return PhoneAuthenticationToken.class.isAssignableFrom(authentication);
}
public UserDetailsService getUserDetailsService() {
return userDetailsService;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
}
第四步重写token
/**
* 电话token
* @Author: 朱维
* @Date 16:29 2019/11/27
*/
public class PhoneAuthenticationToken extends MyAuthenticationToken{
public PhoneAuthenticationToken(Object principal, Object credentials) {
super(principal, credentials);
}
public PhoneAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}
}
第五步UserDetailService修改
/**
* @Author: 朱维
* @Date 17:01 2019/11/27
*/
public abstract class BaseUserDetailService implements UserDetailsService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 用户业务接口
*/
@Autowired
protected UserService userService;
@Autowired
protected TeacherService teacherService;
protected static final String ADMIN_SYS = "ADMIN";
protected static final String TEACHER_SYS = "TEACHER";
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
if(attributes==null){
throw new AuthException("获取不到当先请求");
}
HttpServletRequest request = attributes.getRequest();
String clientId = request.getParameter("client_id");
BaseUser baseUser = getUser(username,clientId);
List<GrantedAuthority> authorities = new ArrayList<>() ;
// 返回带有用户权限信息的User
org.springframework.security.core.userdetails.User user = new org.springframework.security.core.userdetails.User(baseUser.getTelephone(),
baseUser.getPassword(), isActive(baseUser.getStatus()), true, true, true, authorities);
return new BaseUserDetail(baseUser, user);
}
/**
* 获取用户
* @param userName
* @return
*/
protected abstract BaseUser getUser(String userName,String clientId) ;
/**
* 是否有效的
* @param active
* @return
*/
private boolean isActive(Integer active){
if(active==1){
return true;
}
return false;
}
/**
* 判断登录端
* @param client
* @return
*/
protected String judgeTeacherOrAdmin(String client){
if(Constant.ADMIN_CLIENT_ID.equals(client)){
return ADMIN_SYS;
}else if(Constant.TEACHER_CLIENT_ID_START.equals(client)){
return TEACHER_SYS;
}else{
throw new AuthException("非法登录身份登录");
}
}
/**
* @Author: 朱维
* @Date 17:30 2019/11/27
*/
@Service
public class PhoneUserDetailService extends BaseUserDetailService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
protected BaseUser getUser(String userName,String clientId) {
BaseUser baseUser = null;
String sys = judgeTeacherOrAdmin(clientId);
if (ADMIN_SYS.equals(sys)){
baseUser = userService.getUserByTelephone(userName);
baseUser.setType(UserType.ADMIN);
}
if(TEACHER_SYS.equals(sys)){
Teacher teacher = teacherService.getTeacherByTelephone(userName);
baseUser = teacher;
if(teacher.getTeacherRole().equals(TeacherRole.ADMIN)){
baseUser.setType(UserType.TEACHER_ADMIN);
}else{
baseUser.setType(UserType.TEACHER);
}
}
//判断是否请求成功
if (baseUser==null) {
logger.error("用户不存在");
throw new UsernameNotFoundException("用户:" + userName + ",不存在!");
}
return baseUser;
}
}
第六步修改WebSecurityConfig
/**
* 配置spring security
* ResourceServerConfig 是比SecurityConfig 的优先级低的
* @author 大仙
*
*/
@Configuration
@EnableWebSecurity
@Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 用户详情业务实现
*/
@Autowired
private UsernameUserDetailService userDetailsService;
@Autowired
private PhoneUserDetailService phoneUserDetailService;
@Autowired
private QrUserDetailService qrUserDetailService;
@Autowired
private OpenIdUserDetailService openIdUserDetailService;
/**
* 重新实例化bean
*/
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 由于使用的是JWT,我们这里不需要csrf
http.cors().
and().csrf().disable()
.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll().and()
.logout().addLogoutHandler(getLogoutHandler()).logoutSuccessHandler(getLogoutSuccessHandler()).and()
.addFilterBefore(getPhoneLoginAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests().antMatchers("/oauth/**").permitAll().and()
.authorizeRequests().antMatchers("/logout/**").permitAll().and()
.authorizeRequests().antMatchers("/js/**","/favicon.ico").permitAll().and()
.authorizeRequests().antMatchers("/v2/api-docs/**","/webjars/**","/swagger-resources/**","/*.html").permitAll().and()
// 其余所有请求全部需要鉴权认证
.authorizeRequests().anyRequest().authenticated()
;
}
/**
* 用户验证
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(phoneAuthenticationProvider());
auth.authenticationProvider(daoAuthenticationProvider());
auth.authenticationProvider(openIdAuthenticationProvider());
auth.authenticationProvider(qrAuthenticationProvider());
}
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider(){
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
// 设置userDetailsService
provider.setUserDetailsService(userDetailsService);
// 禁止隐藏用户未找到异常
provider.setHideUserNotFoundExceptions(false);
// 使用BCrypt进行密码的hash
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public PhoneAuthenticationProvider phoneAuthenticationProvider(){
PhoneAuthenticationProvider provider = new PhoneAuthenticationProvider();
// 设置userDetailsService
provider.setUserDetailsService(phoneUserDetailService);
// 禁止隐藏用户未找到异常
provider.setHideUserNotFoundExceptions(false);
return provider;
}
/**
* 手机验证码登陆过滤器
* @return
*/
@Bean
public PhoneLoginAuthenticationFilter getPhoneLoginAuthenticationFilter() {
PhoneLoginAuthenticationFilter filter = new PhoneLoginAuthenticationFilter();
try {
filter.setAuthenticationManager(this.authenticationManagerBean());
} catch (Exception e) {
e.printStackTrace();
}
filter.setAuthenticationSuccessHandler(getLoginSuccessAuth());
filter.setAuthenticationFailureHandler(getLoginFailure());
return filter;
}
@Bean
public MyLoginAuthSuccessHandler getLoginSuccessAuth(){
MyLoginAuthSuccessHandler myLoginAuthSuccessHandler = new MyLoginAuthSuccessHandler();
return myLoginAuthSuccessHandler;
}
@Bean
public MyLoginFailureHandler getLoginFailure(){
MyLoginFailureHandler myLoginFailureHandler = new MyLoginFailureHandler();
return myLoginFailureHandler;
}
@Bean
public LogoutHandler getLogoutHandler(){
MyLogoutHandler myLogoutHandler = new MyLogoutHandler();
return myLogoutHandler;
}
@Bean
public LogoutSuccessHandler getLogoutSuccessHandler(){
MyLogoutSuccessHandler logoutSuccessHandler = new MyLogoutSuccessHandler();
return logoutSuccessHandler;
}
}
完事收工