• spring-security-oauth2 中优雅的扩展自定义(短信验证码)登录方式-系列3


    发送验证码的Controller

    首先我们需要创建一个发送验证码的 Controller, 至于如何实现,这里就不多说了,大家都会的,本篇重点说明验证部分.
    【注意】在认证服务器上增加自己的 Controller, 默认情况下访问是返回403,有两种办法解决:

    1. 把认证服务器也配制为资源服务器,既: 它既是认证服务器,也是资源服务器.并配制新增的Controller为任何人都能访问
    2. 关闭认证服务器的 csrf.

    由于我在认证服务器上增加的都是认证相关的功能,任何人都能访问,不需要资源保护,所以我选择了第二种方法.

    关闭认证服务器的 csrf

    在配制认证服务器的时候,我们创建过一个org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter 的子类用于安全配制. 如果你当时没有配制,增加一个就行.

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    @Configuration
    @EnableWebSecurity
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    	@Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable();  // 关闭 csrf
        }
    }
    

      

    增加短信验证码的 TokenGranter

    参考 org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter 的代码

     1 import org.apache.commons.lang3.StringUtils;
     2 import org.springframework.core.env.Environment;
     3 import org.springframework.security.authentication.AbstractAuthenticationToken;
     4 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
     5 import org.springframework.security.core.Authentication;
     6 import org.springframework.security.core.userdetails.UserDetails;
     7 import org.springframework.security.core.userdetails.UserDetailsChecker;
     8 import org.springframework.security.core.userdetails.UserDetailsService;
     9 import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
    10 import org.springframework.security.oauth2.provider.ClientDetails;
    11 import org.springframework.security.oauth2.provider.ClientDetailsService;
    12 import org.springframework.security.oauth2.provider.OAuth2Authentication;
    13 import org.springframework.security.oauth2.provider.OAuth2Request;
    14 import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
    15 import org.springframework.security.oauth2.provider.TokenRequest;
    16 import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
    17 import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
    18 
    19 public class SMSCodeTokenGranter extends AbstractTokenGranter {
    20 
    21     private static final String GRANT_TYPE = "sms_code";
    22 
    23     public SMSCodeLoginTokenGranter(AuthorizationServerTokenServices tokenServices,
    24         ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
    25     super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
    26     }
    27 
    28     @Override
    29     protected OAuth2Authentication getOAuth2Authentication(ClientDetails client,
    30         TokenRequest tokenRequest) {
    31     
    32     Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
    33     String userMobileNo = parameters.get("username");  //客户端提交的用户名
    34     String smscode = parameters.get("smscode");  //客户端提交的验证码
    35     
    36     // 从库里查用户
    37     UserDetails user = 从库里查找用户的代码略;
    38     if(user == null) {
    39         throw new InvalidGrantException("用户不存在");
    40     }
    41     
    42     验证用户状态(是否警用等),代码略
    43 
    44     // 验证验证码
    45     String smsCodeCached = 获取服务中保存的用户验证码,代码略.一般我们是在生成好后放到缓存中
    46     if(StringUtils.isBlank(smsCodeCached)) {
    47         throw new InvalidGrantException("用户没有发送验证码");
    48     }
    49     if(!smscode.equals(smsCodeCached)) {
    50         throw new InvalidGrantException("验证码不正确");
    51     }else {
    52         验证通过后从缓存中移除验证码,代码略
    53     }
    54 
    55     
    56     Authentication userAuth = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
    57     // 关于user.getAuthorities(): 我们的自定义用户实体是实现了 
    58     // org.springframework.security.core.userdetails.UserDetails 接口的, 所以有 user.getAuthorities()
    59     // 当然该参数传null也行
    60     ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
    61     
    62     OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);        
    63     return new OAuth2Authentication(storedOAuth2Request, userAuth);
    64     }
    65 
    66 }

    把 SMSCodeTokenGranter 加入到 CompositeTokenGranter 需要的 List 中

    在上一篇中我们修改了 OAuth2AuthorizationServerConfig类,现在继续修改:
    我们在 getDefaultTokenGranters 方法中加入: getDefaultTokenGranters 的完整代码:

    private List<TokenGranter> getDefaultTokenGranters() {
    	ClientDetailsService clientDetails = clientDetailsService();
    	AuthorizationServerTokenServices tokenServices = tokenServices();
    	AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices();
    	OAuth2RequestFactory requestFactory = requestFactory();
    
    	List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();
    	tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices,
    		authorizationCodeServices, clientDetails, requestFactory));
    	tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory));
    	ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, clientDetails,
    		requestFactory);
    	tokenGranters.add(implicit);
    	tokenGranters.add(
    		new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory));
    	if (authenticationManager != null) {
    	    tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager,
    		    tokenServices, clientDetails, requestFactory));
    	}
    	
    	tokenGranters.add(new SMSCodeLoginTokenGranter(tokenServices, clientDetails, requestFactory, userDetailsService));
    	return tokenGranters;
        }
    

      

    其他代码不用修改

    如何使用我们新增的短信验证码方式?

    我们调用 /oauth/token 进行认证的时候,有一个 grant_type 参数,我们把它的值改为 sms_code
    password 参数可以不要了,新增一个 smscode 参数
    当然,上面的 “sms_code” 和 “smscode” 也是可以修改的:
    “sms_code” 对应 SMSCodeTokenGranter 中的静态常量 GRANT_TYPE
    “smscode” 对应 SMSCodeTokenGranter.getOAuth2Authentication 方法中的 parameters.get(“smscode”);

  • 相关阅读:
    java后台读取配置文件
    冒泡排序
    均分火柴
    Dos 批处理 Shutdown
    时间复杂度分析
    Python冒泡排序
    Python装饰器
    获取状态栏高度
    利用zxing生成二维码
    Android下利用zxing类库实现扫一扫
  • 原文地址:https://www.cnblogs.com/grimm/p/13518344.html
Copyright © 2020-2023  润新知