Spring Security Oauth2 扩展登录方式

第一步:新建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;
	}
}

完事收工

发布了149 篇原创文章 · 获赞 36 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/zhuwei_clark/article/details/103980279