SpringSecurityOAuth2(7) 账号密码登录、手机验证码登录

GitHub地址

码云地址

SpringSecurity 调用流程: SpringSecurityOAuth2(7) 账号密码登录、手机验证码登录_第1张图片

首先会进入UsernamePasswordAuthenticationFilter并且设置权限为null和是否授权为false,然后进入ProviderManager查找支持UsernamepasswordAuthenticationToken的provider并且调用provider.authenticate(authentication);再然后就是UserDetailsService接口的实现类,然后回调UsernamePasswordAuthenticationFilter并且设置权限(具体业务所查出的权限)和设置授权为true。

下面是实现手机验证码登录的流程:

第一步:新建MyAuthenticationToken 自定义AbstractAuthenticationToken

/**
* @Description 自定义AbstractAuthenticationToken
* @Author wwz
* @Date 2019/08/04
* @Param
* @Return
*/
public class MyAuthenticationToken extends AbstractAuthenticationToken {

    private static final long serialVersionUID = 110L;
    protected final Object principal;
    protected Object credentials;

    /**
     * This constructor can be safely used by any code that wishes to create a
     * UsernamePasswordAuthenticationToken, as the {@link
     * #isAuthenticated()} will return false.
     *
     */
    public MyAuthenticationToken(Object principal, Object credentials) {
        super(null);
        this.principal = principal;
        this.credentials = credentials;
        this.setAuthenticated(false);
    }

    /**
     * This constructor should only be used by AuthenticationManager or AuthenticationProvider
     * implementations that are satisfied with producing a trusted (i.e. {@link #isAuthenticated()} = true)
     * token token.
     *
     * @param principal
     * @param credentials
     * @param authorities
     */
    public MyAuthenticationToken(Object principal, Object credentials, Collection authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        super.setAuthenticated(true);
    }


    @Override
    public Object getCredentials() {
        return this.credentials;
    }

    @Override
    public Object getPrincipal() {
        return this.principal;
    }


    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;
    }
}

第二步:新建 (手机验证码登录用)MyPhoneAuthenticationToken 继承 MyAuthenticationToken

/**  
* @Description 手机验证token
* @Author wwz
* @Date 2019/08/04 
* @Param   
* @Return   
*/
public class MyPhoneAuthenticationToken extends MyAuthenticationToken {

    public MyPhoneAuthenticationToken(Object principal, Object credentials) {
        super(principal, credentials);
    }

    public MyPhoneAuthenticationToken(Object principal, Object credentials, Collection authorities) {
        super(principal, credentials, authorities);
    }

}

第三步:新建MyAbstractUserDetailsAuthenticationProvider 抽象类 自定义AuthenticationProvider 抽象,方便其他扩展

/**  
* @Description 自定义AuthenticationProvider 抽象,方便其他扩展
* @Author wwz
* @Date 2019/08/04 
* @Param   
* @Return   
*/ 
public abstract class MyAbstractUserDetailsAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {

    protected final Log logger = LogFactory.getLog(this.getClass());
    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    private UserCache userCache = new NullUserCache();
    private boolean forcePrincipalAsString = false;
    protected boolean hideUserNotFoundExceptions = true;
    private UserDetailsChecker preAuthenticationChecks = new MyAbstractUserDetailsAuthenticationProvider.DefaultPreAuthenticationChecks();
    private UserDetailsChecker postAuthenticationChecks = new MyAbstractUserDetailsAuthenticationProvider.DefaultPostAuthenticationChecks();
    private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();


    protected abstract void additionalAuthenticationChecks(UserDetails var1, Authentication var2) throws AuthenticationException;

    public final void afterPropertiesSet() throws Exception {
        Assert.notNull(this.userCache, "A user cache must be set");
        Assert.notNull(this.messages, "A message source must be set");
        this.doAfterPropertiesSet();
    }

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getPrincipal() == null?"NONE_PROVIDED":authentication.getName();
        boolean cacheWasUsed = true;
        UserDetails user = this.userCache.getUserFromCache(username);
        if(user == null) {
            cacheWasUsed = false;

            try {
                user = this.retrieveUser(username, authentication);
            } catch (UsernameNotFoundException var6) {
                this.logger.debug("User \'" + username + "\' not found");
                if(this.hideUserNotFoundExceptions) {
                    throw new BadCredentialsException(this.messages.getMessage("MyAbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
                }

                throw var6;
            }

            Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
        }

        try {
            this.preAuthenticationChecks.check(user);
            this.additionalAuthenticationChecks(user, authentication);
        } catch (AuthenticationException var7) {
            if(!cacheWasUsed) {
                throw var7;
            }

            cacheWasUsed = false;
            user = this.retrieveUser(username, authentication);
            this.preAuthenticationChecks.check(user);
            this.additionalAuthenticationChecks(user, authentication);
        }

        this.postAuthenticationChecks.check(user);
        if(!cacheWasUsed) {
            this.userCache.putUserInCache(user);
        }

        Object principalToReturn = user;
        if(this.forcePrincipalAsString) {
            principalToReturn = user.getUsername();
        }

        return this.createSuccessAuthentication(principalToReturn, authentication, user);
    }

    protected abstract Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user);

    protected void doAfterPropertiesSet() throws Exception {
    }

    public UserCache getUserCache() {
        return this.userCache;
    }

    public boolean isForcePrincipalAsString() {
        return this.forcePrincipalAsString;
    }

    public boolean isHideUserNotFoundExceptions() {
        return this.hideUserNotFoundExceptions;
    }

    protected abstract UserDetails retrieveUser(String var1, Authentication var2) throws AuthenticationException;

    public void setForcePrincipalAsString(boolean forcePrincipalAsString) {
        this.forcePrincipalAsString = forcePrincipalAsString;
    }

    public void setHideUserNotFoundExceptions(boolean hideUserNotFoundExceptions) {
        this.hideUserNotFoundExceptions = hideUserNotFoundExceptions;
    }

    public void setMessageSource(MessageSource messageSource) {
        this.messages = new MessageSourceAccessor(messageSource);
    }

    public void setUserCache(UserCache userCache) {
        this.userCache = userCache;
    }


    protected UserDetailsChecker getPreAuthenticationChecks() {
        return this.preAuthenticationChecks;
    }

    public void setPreAuthenticationChecks(UserDetailsChecker preAuthenticationChecks) {
        this.preAuthenticationChecks = preAuthenticationChecks;
    }

    protected UserDetailsChecker getPostAuthenticationChecks() {
        return this.postAuthenticationChecks;
    }

    public void setPostAuthenticationChecks(UserDetailsChecker postAuthenticationChecks) {
        this.postAuthenticationChecks = postAuthenticationChecks;
    }

    public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
        this.authoritiesMapper = authoritiesMapper;
    }

    private class DefaultPostAuthenticationChecks implements UserDetailsChecker {
        private DefaultPostAuthenticationChecks() {
        }

        public void check(UserDetails user) {
            if(!user.isCredentialsNonExpired()) {
                MyAbstractUserDetailsAuthenticationProvider.this.logger.debug("User account credentials have expired");
                throw new CredentialsExpiredException(MyAbstractUserDetailsAuthenticationProvider.this.messages.getMessage("MyAbstractUserDetailsAuthenticationProvider.credentialsExpired", "User credentials have expired"));
            }
        }
    }

    private class DefaultPreAuthenticationChecks implements UserDetailsChecker {
        private DefaultPreAuthenticationChecks() {
        }

        public void check(UserDetails user) {
            if(!user.isAccountNonLocked()) {
                MyAbstractUserDetailsAuthenticationProvider.this.logger.debug("User account is locked");
                throw new LockedException(MyAbstractUserDetailsAuthenticationProvider.this.messages.getMessage("MyAbstractUserDetailsAuthenticationProvider.locked", "User account is locked"));
            } else if(!user.isEnabled()) {
                MyAbstractUserDetailsAuthenticationProvider.this.logger.debug("User account is disabled");
                throw new DisabledException(MyAbstractUserDetailsAuthenticationProvider.this.messages.getMessage("MyAbstractUserDetailsAuthenticationProvider.disabled", "User is disabled"));
            } else if(!user.isAccountNonExpired()) {
                MyAbstractUserDetailsAuthenticationProvider.this.logger.debug("User account is expired");
                throw new AccountExpiredException(MyAbstractUserDetailsAuthenticationProvider.this.messages.getMessage("MyAbstractUserDetailsAuthenticationProvider.expired", "User account has expired"));
            }
        }
    }
}

第四步:新建 MyPhoneAuthenticationProvider(手机验证码登录 )继承 MyAbstractUserDetailsAuthenticationProvider ,在这里使用MyPhoneAuthenticationToken,注入参数,同时在这里进行手机验证码验证等,为了方便我这里直接写死了。

/**
* @Description 手机验证码登录
* @Author wwz
* @Date 2019/08/04
* @Param
* @Return
*/
public class MyPhoneAuthenticationProvider extends MyAbstractUserDetailsAuthenticationProvider {

    private UserDetailsService userDetailsService;

    @Override
    protected void additionalAuthenticationChecks(UserDetails var1, Authentication authentication) throws AuthenticationException {

        if(authentication.getPrincipal() == null){
            throw new BadCredentialsException(this.messages.getMessage("MyPhoneAuthenticationProvider.badPrincipal", "Bad badPrincipal"));
        }
        if (authentication.getCredentials() == null) {
            throw new BadCredentialsException(this.messages.getMessage("MyPhoneAuthenticationProvider.badCredentials", "Bad credentials"));
        } else {
            String phoneCode = authentication.getCredentials().toString();
//            String phoneNumber = authentication.getPrincipal().toString();
//
//            String old_code = (String) redisTemplate.opsForValue().get(phoneNumber+"_code");
//
//            if(old_code==null){
//                // 验证码未获取或已经失效
//                throw new BadCredentialsException(this.messages.getMessage("MyPhoneAuthenticationProvider.badCredentials", "Bad phoneCode"));
//            }
//            if(!phoneCode.equals(old_code)){
//                // 验证码错误
//                throw new BadCredentialsException(this.messages.getMessage("MyPhoneAuthenticationProvider.badCredentials", "Bad phoneCode"));
//            }
            if (!"1234".equals(phoneCode)) {
                throw new BadCredentialsException(this.messages.getMessage("MyPhoneAuthenticationProvider.badCredentials", "Bad phoneCode"));
            }
        }
    }

    @Override
    protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
        MyPhoneAuthenticationToken result = new MyPhoneAuthenticationToken(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 MyPhoneAuthenticationToken.class.isAssignableFrom(authentication);
    }


    public UserDetailsService getUserDetailsService() {
        return userDetailsService;
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }
}

第五步: 修改MyUserDetailsService,改为抽象类,使用模板方法模式: protected abstract AuthUser getUser(String var1); 来获取不同数据来源

/**
 * @Description 自定义用户验证数据
 * @Author wwz
 * @Date 2019/07/28
 * @Param
 * @Return
 */

@Service
public abstract class MyUserDetailsService implements UserDetailsService {

    @Autowired
    protected AuthUserMapper authUserMapper;

    @Override
    public UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException {
        // 自定义用户权限数据
        AuthUser authUser1 = getUser(var1);

        AuthUser authUser = authUserMapper.selectById(authUser1.getId());
        if (authUser == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        if (!authUser.getValid()) {
            throw new UsernameNotFoundException("用户不可用");
        }
        Set grantedAuthorities = new HashSet<>();
        if (authUser.getAuthRoles() != null) {
            for (AuthRole role : authUser.getAuthRoles()) {
                // 当前角色可用
                if (role.getValid()) {
                    //角色必须是ROLE_开头
                    GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getRoleName());
                    grantedAuthorities.add(grantedAuthority);
                    if (role.getAuthPermissions() != null) {
                        for (AuthPermission permission : role.getAuthPermissions()) {
                            // 当前权限可用
                            if (permission.getValid()) {
                                // 拥有权限设置为   auth/member/GET  可以访问auth服务下面 member的查询方法
                                GrantedAuthority authority = new SimpleGrantedAuthority(permission.getServicePrefix() + "/" + permission.getUri() + "/" + permission.getMethod());
                                grantedAuthorities.add(authority);
                            }
                        }
                    }
                }
                //获取权限
            }
        }
        MyUserDetails userDetails = new MyUserDetails(authUser, grantedAuthorities);
        return userDetails;
    }


    protected abstract AuthUser getUser(String var1);
}

继续第五步:

新建MyUsernameUserDetailsService继承 MyUsernameUserDetailsService 该方法为原来的登录提供数据

@Service
public class MyUsernameUserDetailsService extends MyUserDetailsService {
    @Override
    protected AuthUser getUser(String var1) {
        // 账号密码登录根据用户名查询用户
        AuthUser authUser = authUserMapper.selectByUsername(var1);
        if (authUser == null) {
            throw new UsernameNotFoundException("找不到该用户,用户名:" + var1);
        }
        return authUser;
    }
}

新建MyPhoneUserDetailsService 继承MyUsernameUserDetailsService 该方法为新的手机验证码登录提供数据。

@Service
public class MyPhoneUserDetailsService extends MyUserDetailsService {
    @Override
    protected AuthUser getUser(String var1) {
        // 手机验证码登录使用,根据手机号码查询用户信息
        AuthUser authUser = authUserMapper.selectByPhone(var1);
        if (authUser == null) {
            throw new UsernameNotFoundException("找不到该用户,手机号码有误:" + var1);
        }
        return authUser;
    }
}

第六步:新建MyLoginAuthSuccessHandler 登录成功处理器,该方法用于验证client信息 并返回token信息。

/**
 * @Description 登录成功处理器
 * @Author wwz
 * @Date 2019/08/04
 * @Param
 * @Return
 */
@Component
public class MyLoginAuthSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    @Autowired
    private MyClientDetailsService clientDetailsService;

    @Autowired
    private AuthorizationServerTokenServices authorizationServerTokenServices;

    @Bean
    public MyClientDetailsService clientDetailsService(){
        return new MyClientDetailsService();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        String header = request.getHeader("Authorization");
        if (header == null && !header.startsWith("Basic")) {
            throw new UnapprovedClientAuthenticationException("请求投中无client信息");
        }
        String tmp = header.substring(6);
        String defaultClientDetails = new String(Base64.getDecoder().decode(tmp));

        String[] clientArrays = defaultClientDetails.split(":");
        String clientId = clientArrays[0].trim();
        String clientSecret = clientArrays[1].trim();


        ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);

        if (clientDetails == null) {
            throw new UnapprovedClientAuthenticationException("clientId 不存在" + clientId);
            //判断  方言  是否一致
        } else if (!passwordEncoder().matches(clientSecret, clientDetails.getClientSecret())) {
            throw new UnapprovedClientAuthenticationException("clientSecret 不匹配" + clientId);
        }
        TokenRequest tokenRequest = new TokenRequest(null, clientId, clientDetails.getScope(), "custom");

        OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);

        OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);

        OAuth2AccessToken token = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);

        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(JSONObject.toJSONString(token));
    }
}

第七步:新建MyLoginAuthFailureHandler登录失败处理器,返回失败异常,该异常为第四步抛出异常

/**
 * @Description 登录失败处理器
 * @Author wwz
 * @Date 2019/08/05
 * @Param
 * @Return
 */
@Component
public class MyLoginAuthFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        ResponseVo responseVo = new ResponseVo();
        responseVo.setCode(401);
        responseVo.setMessage(exception.getMessage());
        responseVo.setData("path:"+request.getRequestURI());
        response.setStatus(401);
        HttpUtilsResultVO.writerError(responseVo, response);
    }
}

第八步: 新建MyPhoneLoginAuthenticationFilter 手机验证码登录过滤器,拦截登录的url,进行数据注入到MyPhoneAuthenticationToken。

/**
* @Description 手机验证码:post /token/login?type=phoneNumber&phoneNumber=15000000000&phoneCode=1234
* @Author wwz
* @Date 2019/08/04
* @Param
* @Return
*/
public class MyPhoneLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    private static final String PHONE_NUMBER_KEY = "phoneNumber"; // 手机号码

    private static final String PHONE_NUMBER_CODE_KEY = "phoneCode"; // 验证码

    private boolean postOnly = true;

    private static final String LOGIN_URL = "/token/phoneLogin";

    public MyPhoneLoginAuthenticationFilter() {
        super(new AntPathRequestMatcher(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 =RequestUtil(request,PHONE_NUMBER_KEY);
        String credentials =RequestUtil(request,PHONE_NUMBER_CODE_KEY);



        principal = principal.trim();
        authRequest = new MyPhoneAuthenticationToken(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 RequestUtil(HttpServletRequest request, String parameter) {
        String result = request.getParameter(parameter);
        return result == null ? "" : result;
    }
}

第九步: 重点 修改MySecurityConfig 配置,装配 两个数据接口,装配 登录成功处理器 装配配置,以及把MyPhoneLoginAuthenticationFilter加入到过滤链。

/**
 * @Description security 配置
 * ResourceServerConfigurerAdapter 是比WebSecurityConfigurerAdapter 的优先级低的
 * @Author wwz
 * @Date 2019/07/28
 * @Param
 * @Return
 */
@Configuration
@EnableWebSecurity
@Order(2)  // WebSecurityConfigurerAdapter 默认为100 这里配置为2设置比资源认证器高
public class MySecurityConfig extends WebSecurityConfigurerAdapter {

    // 自定义用户验证数据
    @Autowired
    private MyUsernameUserDetailsService myUsernameUserDetailsService;

    @Autowired
    private MyPhoneUserDetailsService myPhoneUserDetailsService;


    // 装配登录成功处理器 生成token用 通用, 下方配置的时候不能用new 的形式加入 不然里面的接口注入会报空指针
    @Autowired
    private MyLoginAuthSuccessHandler myLoginAuthSuccessHandler;

    @Autowired
    private MyLoginAuthFailureHandler myLoginAuthFailureHandler;   // 配置登录失败处理器

    // 加密方式
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // 验证器加载
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .addFilterBefore(getPhoneLoginAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                // 匹配oauth相关,匹配健康,匹配默认登录登出 在httpSecurity处理,,其他到ResourceServerConfigurerAdapter OAuth2处理  1
                .requestMatchers().antMatchers("/oauth/**", "/actuator/health", "/client/**","/token/phoneLogin")
                .and()
                // 匹配的全部无条件通过 permitAll 2
                .authorizeRequests().antMatchers("/oauth/**", "/actuator/health", "/client/**","/token/phoneLogin").permitAll()
                // 匹配条件1的 并且不再条件2通过范围内的其他url全部需要验证登录
                .and().authorizeRequests().anyRequest().authenticated()
                // 启用登录验证
                .and().formLogin().permitAll();
        // 不启用 跨站请求伪造 默认为启用, 需要启用的话得在form表单中生成一个_csrf
        http.csrf().disable();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(daoAuthenticationProvider());
        auth.authenticationProvider(myPhoneAuthenticationProvider());
    }

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        // 设置userDetailsService
        provider.setUserDetailsService(myUsernameUserDetailsService);
        // 禁止隐藏用户未找到异常
        provider.setHideUserNotFoundExceptions(false);
        // 使用BCrypt进行密码的hash
        provider.setPasswordEncoder(passwordEncoder());
        return provider;
    }

    @Bean
    public MyPhoneAuthenticationProvider myPhoneAuthenticationProvider() {
        MyPhoneAuthenticationProvider provider = new MyPhoneAuthenticationProvider();
        provider.setUserDetailsService(myPhoneUserDetailsService);
        provider.setHideUserNotFoundExceptions(false);
        return provider;
    }

    /**
     * 手机验证码登陆过滤器
     */
    @Bean
    public MyPhoneLoginAuthenticationFilter getPhoneLoginAuthenticationFilter() {
        MyPhoneLoginAuthenticationFilter filter = new MyPhoneLoginAuthenticationFilter();
        try {
            filter.setAuthenticationManager(this.authenticationManagerBean());
        } catch (Exception e) {
            e.printStackTrace();
        }
        filter.setAuthenticationSuccessHandler(myLoginAuthSuccessHandler);
        filter.setAuthenticationFailureHandler(myLoginAuthFailureHandler);
        return filter;
    }
}

验证码错误返回:

SpringSecurityOAuth2(7) 账号密码登录、手机验证码登录_第2张图片

登录成功返回:

SpringSecurityOAuth2(7) 账号密码登录、手机验证码登录_第3张图片

最后,原来登录方式,因为修改了 MyUserDetailsService 接口,无法为原来的登录方式提供数据,所以改为MyUsernameUserDetailsService来提供数据,在MySecurityOAuth2Config中调整

补充,因为自定义了MyPhoneAuthenticationToken,在资源服务器中使用该模式的token需要把文件放置到对应的资源项目中,不然会报找不到文件异常。

账号密码登录,可以参照步骤执行,或者按前面的方法来完成。

转载于:https://my.oschina.net/u/3500033/blog/3083838

你可能感兴趣的:(SpringSecurityOAuth2(7) 账号密码登录、手机验证码登录)