#103 初步完成双重验证

This commit is contained in:
zhouhao
2018-12-04 18:39:04 +08:00
parent 106006cf10
commit 768033f221
46 changed files with 1459 additions and 94 deletions

View File

@@ -31,4 +31,5 @@ public class AopAuthorizeAutoConfiguration {
return new AopAuthorizingController(authorizingHandler, aopMethodAuthorizeDefinitionParser);
}
}

View File

@@ -8,17 +8,19 @@ import org.hswebframework.web.authorization.basic.embed.EmbedAuthenticationManag
import org.hswebframework.web.authorization.basic.handler.DefaultAuthorizingHandler;
import org.hswebframework.web.authorization.basic.handler.UserAllowPermissionHandler;
import org.hswebframework.web.authorization.basic.handler.access.DefaultDataAccessController;
import org.hswebframework.web.authorization.basic.twofactor.TwoFactorHandlerInterceptorAdapter;
import org.hswebframework.web.authorization.basic.web.*;
import org.hswebframework.web.authorization.basic.web.session.UserTokenAutoExpiredListener;
import org.hswebframework.web.authorization.token.UserTokenManager;
import org.hswebframework.web.authorization.twofactor.TwoFactorValidatorManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@@ -58,6 +60,20 @@ public class AuthorizingHandlerAutoConfiguration {
@Bean
@ConditionalOnProperty(prefix = "hsweb.authorize.two-factor", name = "enable", havingValue = "true")
@Order(100)
public WebMvcConfigurer twoFactorHandlerConfigurer(TwoFactorValidatorManager manager) {
return new WebMvcConfigurerAdapter() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TwoFactorHandlerInterceptorAdapter(manager));
super.addInterceptors(registry);
}
};
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public WebMvcConfigurer webUserTokenInterceptorConfigurer(UserTokenManager userTokenManager,
AopMethodAuthorizeDefinitionParser parser,
List<UserTokenParser> userTokenParser) {

View File

@@ -0,0 +1,52 @@
package org.hswebframework.web.authorization.basic.twofactor;
import lombok.AllArgsConstructor;
import org.hswebframework.web.authorization.Authentication;
import org.hswebframework.web.authorization.User;
import org.hswebframework.web.authorization.annotation.TwoFactor;
import org.hswebframework.web.authorization.exception.NeedTwoFactorException;
import org.hswebframework.web.authorization.twofactor.TwoFactorValidator;
import org.hswebframework.web.authorization.twofactor.TwoFactorValidatorManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author zhouhao
* @since 3.0.4
*/
@AllArgsConstructor
public class TwoFactorHandlerInterceptorAdapter extends HandlerInterceptorAdapter {
private TwoFactorValidatorManager validatorManager;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod method = ((HandlerMethod) handler);
TwoFactor factor = method.getMethodAnnotation(TwoFactor.class);
if (factor == null || factor.ignore()) {
return true;
}
String userId = Authentication.current()
.map(Authentication::getUser)
.map(User::getId)
.orElse(null);
TwoFactorValidator validator = validatorManager.getValidator(userId, factor.value(), factor.provider());
if (!validator.expired()) {
return true;
}
String code = request.getParameter(factor.parameter());
if (StringUtils.isEmpty(code)) {
throw new NeedTwoFactorException("需要进行双重验证", factor.provider());
} else if (!validator.verify(code, factor.timeout())) {
throw new NeedTwoFactorException("验证码错误", factor.provider());
}
}
return super.preHandle(request, response, handler);
}
}

View File

@@ -51,6 +51,29 @@ class FullFunctionTest extends Specification {
}
def "测试双重验证"() {
given: "登录"
def token = doLogin("admin", "admin")
when: "登录成功"
token != null
then: "调用双重验证接口"
mockMvc.perform(get("/test/two-factor")
.header("token", token))
.andExpect(status().is(403))
.andReturn()
.getResponse()
.getContentAsString()
def resp = mockMvc.perform(get("/test/two-factor")
.header("token", token)
.param("verifyCode", "test"))
.andExpect(status().is(200))
.andReturn()
.getResponse()
.getContentAsString()
expect:
resp != null
}
def "测试查询"() {
given: "登录"
def token = doLogin("admin", "admin")

View File

@@ -1,8 +1,10 @@
package org.hswebframework.web.authorization.full.controller;
import org.hswebframework.web.authorization.annotation.Authorize;
import org.hswebframework.web.authorization.annotation.TwoFactor;
import org.hswebframework.web.authorization.full.controller.model.TestModel;
import org.hswebframework.web.controller.message.ResponseMessage;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -21,4 +23,10 @@ public class TestCrudController implements CrudController<TestModel> {
return ResponseMessage.ok();
}
@TwoFactor(value = "test", provider = "test")
@GetMapping("/two-factor")
public ResponseMessage<String> testTowFactor() {
return ResponseMessage.ok();
}
}

View File

@@ -0,0 +1,39 @@
package org.hswebframework.web.authorization.full.controller;
import org.hswebframework.web.authorization.twofactor.TwoFactorValidator;
import org.hswebframework.web.authorization.twofactor.TwoFactorValidatorProvider;
import org.springframework.stereotype.Component;
/**
* @author zhouhao
* @since 3.0.4
*/
@Component
public class TestTwoFactorValidatorProvider implements TwoFactorValidatorProvider {
@Override
public String getProvider() {
return "test";
}
@Override
public TwoFactorValidator createTwoFactorValidator(String userId, String operation) {
return new TwoFactorValidator() {
boolean success = false;
@Override
public String getProvider() {
return "test";
}
@Override
public boolean verify(String code, long timeout) {
return success = code.equalsIgnoreCase("test");
}
@Override
public boolean expired() {
return !success;
}
};
}
}

View File

@@ -1,59 +1,60 @@
spring:
aop:
auto: true
proxy-target-class: true
datasource:
url : jdbc:h2:mem:example-oauth2-client
username : sa
password :
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name : org.h2.Driver
cache:
type: simple
aop:
auto: true
proxy-target-class: true
datasource:
url: jdbc:h2:mem:example-oauth2-client
username: sa
password:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.h2.Driver
cache:
type: simple
hsweb:
app:
name: hsweb-oauth2 客户端示例
version: 3.0.0
authorize:
allows:
users:
admin: "**.TestController.*"
users:
admin:
name: 超级管理员
username: admin
password: admin
roles: #用户的角色
- id: admin
name: 管理员
- id: user
name: 用户
permissions-simple:
test: query,get
permissions:
- id: user-manager
actions: query,get,update,delete
dataAccesses:
- action: query
type: DENY_FIELDS
fields:
- password
- salt
- id: test
actions: query,add,update
dataAccesses:
- action: query
type: DENY_FIELDS
fields:
- password
- action: update
type: DENY_FIELDS
fields:
- name
- action: add
type: DENY_FIELDS
fields:
- id
app:
name: hsweb-oauth2 客户端示例
version: 3.0.0
authorize:
allows:
users:
admin: "**.TestController.*"
two-factor:
enable: true
users:
admin:
name: 超级管理员
username: admin
password: admin
roles: #用户的角色
- id: admin
name: 管理员
- id: user
name: 用户
permissions-simple:
test: query,get
permissions:
- id: user-manager
actions: query,get,update,delete
dataAccesses:
- action: query
type: DENY_FIELDS
fields:
- password
- salt
- id: test
actions: query,add,update
dataAccesses:
- action: query
type: DENY_FIELDS
fields:
- password
- action: update
type: DENY_FIELDS
fields:
- name
- action: add
type: DENY_FIELDS
fields:
- id
server:
port: 8808