mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-05-23 01:39:35 +08:00
#103 初步完成双重验证
This commit is contained in:
@@ -31,4 +31,5 @@ public class AopAuthorizeAutoConfiguration {
|
||||
|
||||
return new AopAuthorizingController(authorizingHandler, aopMethodAuthorizeDefinitionParser);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user