Authenticator Code认证找代码一般在哪找那里找?


1.背景google auth 做为二次认证,大多场景下都使用在ssh 登陆下,并且在ssh 的场景下使用,搭建相对比较简单,本文将介绍google auth 使用在应用平台的二次认证,如:单点登陆,网站登陆等平台,增长平台的安全性认证。#ssh 的搭建能够参考另一篇博客 http://www.noobyard.com/article/p-gspmzflb-dw.html
2.实现原理1.使用pyotp 的python模块生成google auth 须要的密钥2.根据密钥生成条形码图片3.使用google authenticator 客户端扫描条形码,客户端根据时间及密钥通过算法生成6位数的验证码4.平台二次认证经过对输入的验证码进行校验,校验也是基于时间和密钥前端
3.代码实现
a.密钥生成import pyotpgtoken = pyotp.random_base32() #获取随机密钥,存于用户表中
b.生成条形码图片,根据用户名及密钥生成条形码图片from qrcode import QRCode,constantsdef get_qrcode(secret_key,username):BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(file)))filepath = BASE_DIR + '/app/static/img/qrcode/'data = pyotp.totp.TOTP(secret_key).provisioning_uri(username, issuer_name="Verfiy Code")qr = QRCode(version=1,error_correction=constants.ERROR_CORRECT_L,box_size=6,border=4,)try:qr.add_data(data)qr.make(fit=True)img = qr.make_image()img.save(filepath+secret_key+'.png') #保存条形码图片return Trueexcept Exception,e:return False
c.客户扫描图片,前端页面验证用户名和密码后,显示对应的条形码图片参考另一篇博客:http://www.noobyard.com/article/p-atcijuaq-dc.html
d.校验验证码的正确性import pyotpdef Google_Verify_Result(secret_key,verifycode):t = pyotp.TOTP(secret_key)result = t.verify(verifycode) #对输入验证码进行校验,正确返回Truemsg = result if result is True else
Falsereturn msg
相关文章>>更多相关文章<<
}

前言
基于SpringCloud做微服务架构分布式系统时,OAuth2.0作为认证的业内标准,Spring Security OAuth2也提供了全套的解决方案来支持在Spring Cloud/Spring Boot环境下使用OAuth2.0,提供了开箱即用的组件。但是在开发过程中我们会发现由于Spring Security
OAuth2的组件特别全面,这样就导致了扩展很不方便或者说是不太容易直指定扩展的方案,例如:
图片验证码登录
短信验证码登录
微信小程序登录
第三方系统登录
CAS单点登录
在面对这些场景的时候,预计很多对Spring Security OAuth2不熟悉的人恐怕会无从下手。基于上述的场景要求,如何优雅的集成短信验证码登录及第三方登录,怎么样才算是优雅集成呢?有以下要求:
不侵入Spring Security OAuth2的原有代码
对于不同的登录方式不扩展新的端点,使用/oauth/token可以适配所有的登录方式
可以对所有登录方式进行兼容,抽象一套模型只要简单的开发就可以集成登录
基于上述的设计要求,接下来将会在文章种详细介绍如何开发一套集成登录认证组件开满足上述要求。
阅读本篇文章您需要了解OAuth2.0认证体系、SpringBoot、SpringSecurity以及Spring Cloud等相关知识
思路
我们来看下Spring Security OAuth2的认证流程:
这个流程当中,切入点不多,集成登录的思路如下:
在进入流程之前先进行拦截,设置集成认证的类型,例如:短信验证码、图片验证码等信息。
在拦截的通知进行预处理,预处理的场景有很多,比如验证短信验证码是否匹配、图片验证码是否匹配、是否是登录IP白名单等处理
在UserDetailService.loadUserByUsername方法中,根据之前设置的集成认证类型去获取用户信息,例如:通过手机号码获取用户、通过微信小程序OPENID获取用户等等
接入这个流程之后,基本上就可以优雅集成第三方登录。
实现
介绍完思路之后,下面通过代码来展示如何实现:
第一步,定义拦截器拦截登录的请求
/**
* @author LIQIU
* @date 2018-3-30
**/
@Component
public class IntegrationAuthenticationFilter extends GenericFilterBean implements ApplicationContextAware {
private static final String AUTH_TYPE_PARM_NAME = "auth_type";
private static final String OAUTH_TOKEN_URL = "/oauth/token";
private Collection<IntegrationAuthenticator> authenticators;
private ApplicationContext applicationContext;
private RequestMatcher requestMatcher;
public IntegrationAuthenticationFilter(){
this.requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(OAUTH_TOKEN_URL, "GET"),
new AntPathRequestMatcher(OAUTH_TOKEN_URL, "POST")
);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if(requestMatcher.matches(request)){
//设置集成登录信息
IntegrationAuthentication integrationAuthentication = new IntegrationAuthentication();
integrationAuthentication.setAuthType(request.getParameter(AUTH_TYPE_PARM_NAME));
integrationAuthentication.setAuthParameters(request.getParameterMap());
IntegrationAuthenticationContext.set(integrationAuthentication);
try{
//预处理
this.prepare(integrationAuthentication);
filterChain.doFilter(request,response);
//后置处理
this.complete(integrationAuthentication);
}finally {
IntegrationAuthenticationContext.clear();
}
}else{
filterChain.doFilter(request,response);
}
}
/**
* 进行预处理
* @param integrationAuthentication
*/
private void prepare(IntegrationAuthentication integrationAuthentication) {
//延迟加载认证器
if(this.authenticators == null){
synchronized (this){
Map<String,IntegrationAuthenticator> integrationAuthenticatorMap = applicationContext.getBeansOfType(IntegrationAuthenticator.class);
if(integrationAuthenticatorMap != null){
this.authenticators = integrationAuthenticatorMap.values();
}
}
}
if(this.authenticators == null){
this.authenticators = new ArrayList<>();
}
for (IntegrationAuthenticator authenticator: authenticators) {
if(authenticator.support(integrationAuthentication)){
authenticator.prepare(integrationAuthentication);
}
}
}
/**
* 后置处理
* @param integrationAuthentication
*/
private void complete(IntegrationAuthentication integrationAuthentication){
for (IntegrationAuthenticator authenticator: authenticators) {
if(authenticator.support(integrationAuthentication)){
authenticator.complete(integrationAuthentication);
}
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
在这个类种主要完成2部分工作:1、根据参数获取当前的是认证类型,2、根据不同的认证类型调用不同的IntegrationAuthenticator.prepar进行预处理
第二步,将拦截器放入到拦截链条中
/**
* @author LIQIU
* @date 2018-3-7
**/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private IntegrationUserDetailsService integrationUserDetailsService;
@Autowired
private WebResponseExceptionTranslator webResponseExceptionTranslator;
@Autowired
private IntegrationAuthenticationFilter integrationAuthenticationFilter;
@Autowired
private DatabaseCachableClientDetailsService redisClientDetailsService;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// TODO persist clients details
clients.withClientDetails(redisClientDetailsService);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.tokenStore(new RedisTokenStore(redisConnectionFactory))
//
.accessTokenConverter(jwtAccessTokenConverter())
.authenticationManager(authenticationManager)
.exceptionTranslator(webResponseExceptionTranslator)
.reuseRefreshTokens(false)
.userDetailsService(integrationUserDetailsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients()
.tokenKeyAccess("isAuthenticated()")
.checkTokenAccess("permitAll()")
.addTokenEndpointAuthenticationFilter(integrationAuthenticationFilter);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey("cola-cloud");
return jwtAccessTokenConverter;
}
}
通过调用security. .addTokenEndpointAuthenticationFilter(integrationAuthenticationFilter);方法,将拦截器放入到认证链条中。
第三步,根据认证类型来处理用户信息
@Service
public class IntegrationUserDetailsService implements UserDetailsService {
@Autowired
private UpmClient upmClient;
private List<IntegrationAuthenticator> authenticators;
@Autowired(required = false)
public void setIntegrationAuthenticators(List<IntegrationAuthenticator> authenticators) {
this.authenticators = authenticators;
}
@Override
public User loadUserByUsername(String username) throws UsernameNotFoundException {
IntegrationAuthentication integrationAuthentication = IntegrationAuthenticationContext.get();
//判断是否是集成登录
if (integrationAuthentication == null) {
integrationAuthentication = new IntegrationAuthentication();
}
integrationAuthentication.setUsername(username);
UserVO userVO = this.authenticate(integrationAuthentication);
if(userVO == null){
throw new UsernameNotFoundException("用户名或密码错误");
}
User user = new User();
BeanUtils.copyProperties(userVO, user);
this.setAuthorize(user);
return user;
}
/**
* 设置授权信息
*
* @param user
*/
public void setAuthorize(User user) {
Authorize authorize = this.upmClient.getAuthorize(user.getId());
user.setRoles(authorize.getRoles());
user.setResources(authorize.getResources());
}
private UserVO authenticate(IntegrationAuthentication integrationAuthentication) {
if (this.authenticators != null) {
for (IntegrationAuthenticator authenticator : authenticators) {
if (authenticator.support(integrationAuthentication)) {
return authenticator.authenticate(integrationAuthentication);
}
}
}
return null;
}
}
这里实现了一个IntegrationUserDetailsService ,在loadUserByUsername方法中会调用authenticate方法,在authenticate方法中会当前上下文种的认证类型调用不同的IntegrationAuthenticator 来获取用户信息,接下来来看下默认的用户名密码是如何处理的:
@Component
@Primary
public class UsernamePasswordAuthenticator extends AbstractPreparableIntegrationAuthenticator {
@Autowired
private UcClient ucClient;
@Override
public UserVO authenticate(IntegrationAuthentication integrationAuthentication) {
return ucClient.findUserByUsername(integrationAuthentication.getUsername());
}
@Override
public void prepare(IntegrationAuthentication integrationAuthentication) {
}
@Override
public boolean support(IntegrationAuthentication integrationAuthentication) {
return StringUtils.isEmpty(integrationAuthentication.getAuthType());
}
}
UsernamePasswordAuthenticator只会处理没有指定的认证类型即是默认的认证类型,这个类中主要是通过用户名获取密码。接下来来看下图片验证码登录如何处理的:
/**
* 集成验证码认证
* @author LIQIU
* @date 2018-3-31
**/
@Component
public class VerificationCodeIntegrationAuthenticator extends UsernamePasswordAuthenticator {
private final static String VERIFICATION_CODE_AUTH_TYPE = "vc";
@Autowired
private VccClient vccClient;
@Override
public void prepare(IntegrationAuthentication integrationAuthentication) {
String vcToken = integrationAuthentication.getAuthParameter("vc_token");
String vcCode = integrationAuthentication.getAuthParameter("vc_code");
//验证验证码
Result<Boolean> result = vccClient.validate(vcToken, vcCode, null);
if (!result.getData()) {
throw new OAuth2Exception("验证码错误");
}
}
@Override
public boolean support(IntegrationAuthentication integrationAuthentication) {
return VERIFICATION_CODE_AUTH_TYPE.equals(integrationAuthentication.getAuthType());
}
}
VerificationCodeIntegrationAuthenticator继承UsernamePasswordAuthenticator,因为其只是需要在prepare方法中验证验证码是否正确,获取用户还是用过用户名密码的方式获取。但是需要认证类型为"vc"才会处理接下来来看下短信验证码登录是如何处理的:
@Component
public class SmsIntegrationAuthenticator extends AbstractPreparableIntegrationAuthenticator implements
ApplicationEventPublisherAware {
@Autowired
private UcClient ucClient;
@Autowired
private VccClient vccClient;
@Autowired
private PasswordEncoder passwordEncoder;
private ApplicationEventPublisher applicationEventPublisher;
private final static String SMS_AUTH_TYPE = "sms";
@Override
public UserVO authenticate(IntegrationAuthentication integrationAuthentication) {
//获取密码,实际值是验证码
String password = integrationAuthentication.getAuthParameter("password");
//获取用户名,实际值是手机号
String username = integrationAuthentication.getUsername();
//发布事件,可以监听事件进行自动注册用户
this.applicationEventPublisher.publishEvent(new SmsAuthenticateBeforeEvent(integrationAuthentication));
//通过手机号码查询用户
UserVO userVo = this.ucClient.findUserByPhoneNumber(username);
if (userVo != null) {
//将密码设置为验证码
userVo.setPassword(passwordEncoder.encode(password));
//发布事件,可以监听事件进行消息通知
this.applicationEventPublisher.publishEvent(new SmsAuthenticateSuccessEvent(integrationAuthentication));
}
return userVo;
}
@Override
public void prepare(IntegrationAuthentication integrationAuthentication) {
String smsToken = integrationAuthentication.getAuthParameter("sms_token");
String smsCode = integrationAuthentication.getAuthParameter("password");
String username = integrationAuthentication.getAuthParameter("username");
Result<Boolean> result = vccClient.validate(smsToken, smsCode, username);
if (!result.getData()) {
throw new OAuth2Exception("验证码错误或已过期");
}
}
@Override
public boolean support(IntegrationAuthentication integrationAuthentication) {
return SMS_AUTH_TYPE.equals(integrationAuthentication.getAuthType());
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}
SmsIntegrationAuthenticator会对登录的短信验证码进行预处理,判断其是否非法,如果是非法的则直接中断登录。如果通过预处理则在获取用户信息的时候通过手机号去获取用户信息,并将密码重置,以通过后续的密码校验。
总结
在这个解决方案中,主要是使用责任链和适配器的设计模式来解决集成登录的问题,提高了可扩展性,并对spring的源码无污染。如果还要继承其他的登录,只需要实现自定义的IntegrationAuthenticator就可以。
项目地址:https://gitee.com/leecho/cola...大家有好的建议和想法可以一起沟通交流。
}
登录后你可以不限量看优质回答私信答主深度交流精彩内容一键收藏
查看全部 6 个回答
开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。
实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。
目前客户端有:
android版:
Google 身份验证器
iOS版:
https://itunes.apple.com/cn/app/google-authenticator/id388497605
实现原理:
一、用户需要开启Google Authenticator服务时,
1.服务器随机生成一个类似于『DPI45HKISEXU6HG7』的密钥,并且把这个密钥保存在数据库中。
2.在页面上显示一个二维码,内容是一个URI地址(otpauth://totp/账号?secret=密钥),如『otpauth://totp/kisexu@gmail.com?secret=DPI45HCEBCJK6HG7』,下图:
otpauth://totp/kisexu@gmail.com?secret=DPI45HCEBCJK6HG7 (二维码自动识别)
3.客户端扫描二维码,把密钥『DPI45HKISEXU6HG7』保存在客户端。
二、用户需要登陆时
1.客户端每30秒使用密钥『DPI45HKISEXU6HG7』和时间戳通过一种『算法』生成一个6位数字的一次性密码,如『684060』。如下图android版界面:
2.用户登陆时输入一次性密码『684060』。
3.服务器端使用保存在数据库中的密钥『DPI45HKISEXU6HG7』和时间戳通过同一种『算法』生成一个6位数字的一次性密码。大家都懂控制变量法,如果算法相同、密钥相同,又是同一个时间(时间戳相同),那么客户端和服务器计算出的一次性密码是一样的。服务器验证时如果一样,就登录成功了。
Tips:
1.这种『算法』是公开的,所以服务器端也有很多开源的实现,比如php版的:
https://github.com/PHPGangsta/GoogleAuthenticator
。上github搜索『Google Authenticator』可以找到更多语言版的Google Authenticator。
2.所以,你在自己的项目可以轻松加入对Google Authenticator的支持,在一个客户端上显示多个账户的效果可以看上面android版界面的截图。目前dropbox、lastpass、wordpress,甚至vps等第三方应用都支持Google Authenticator登陆,请自行搜索。
3.现实生活中,网银、网络游戏的实体动态口令牌其实原理也差不多,大家可以自行脑补下,谢谢。
发布于 2013-09-04 19:55查看全部 6 个回答}

我要回帖

更多关于 找代码一般在哪找 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信