增加部分实现

This commit is contained in:
zhou-hao
2019-10-12 20:00:35 +08:00
parent 3b586cbbfd
commit 8ce58b3bec
53 changed files with 1079 additions and 428 deletions

View File

@@ -1,10 +1,12 @@
package org.hswebframework.web.authorization.basic.configuration;
import org.hswebframework.web.authorization.AuthenticationManager;
import org.hswebframework.web.authorization.ReactiveAuthenticationManager;
import org.hswebframework.web.authorization.access.DataAccessController;
import org.hswebframework.web.authorization.access.DataAccessHandler;
import org.hswebframework.web.authorization.basic.aop.AopMethodAuthorizeDefinitionParser;
import org.hswebframework.web.authorization.basic.embed.EmbedAuthenticationManager;
import org.hswebframework.web.authorization.basic.embed.EmbedAuthenticationProperties;
import org.hswebframework.web.authorization.basic.embed.EmbedReactiveAuthenticationManager;
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;
@@ -16,6 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
@@ -33,6 +36,7 @@ import java.util.List;
* @since 3.0
*/
@Configuration
@EnableConfigurationProperties(EmbedAuthenticationProperties.class)
public class AuthorizingHandlerAutoConfiguration {
@Bean
@@ -85,19 +89,19 @@ public class AuthorizingHandlerAutoConfiguration {
AopMethodAuthorizeDefinitionParser parser,
List<UserTokenParser> userTokenParser) {
return new WebMvcConfigurerAdapter() {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new WebUserTokenInterceptor(userTokenManager, userTokenParser, parser));
super.addInterceptors(registry);
}
};
}
@Bean
@ConditionalOnMissingBean(AuthenticationManager.class)
public AuthenticationManager embedAuthenticationManager() {
return new EmbedAuthenticationManager();
@ConditionalOnMissingBean(ReactiveAuthenticationManager.class)
public ReactiveAuthenticationManager embedAuthenticationManager(EmbedAuthenticationProperties properties) {
return new EmbedReactiveAuthenticationManager(properties);
}
@Bean
@@ -130,8 +134,9 @@ public class AuthorizingHandlerAutoConfiguration {
}
@Bean
public UserTokenController userTokenController() {
return new UserTokenController();
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public ReactiveUserTokenController userTokenController() {
return new ReactiveUserTokenController();
}
@Configuration

View File

@@ -60,8 +60,8 @@ public class BasicAuthorizationTokenParser implements UserTokenForTypeParser {
if (usernameAndPassword.contains(":")) {
String[] arr = usernameAndPassword.split("[:]");
Authentication authentication = authenticationManager
.authenticate(Mono.just(new PlainTextUsernamePasswordAuthenticationRequest(arr[0], arr[1])))
.blockOptional().orElse(null);
.authenticate(new PlainTextUsernamePasswordAuthenticationRequest(arr[0], arr[1]))
;
if (authentication != null) {
return new AuthorizedToken() {
@Override

View File

@@ -0,0 +1,112 @@
package org.hswebframework.web.authorization.basic.embed;
import com.alibaba.fastjson.JSON;
import lombok.Getter;
import lombok.Setter;
import org.hswebframework.web.authorization.Authentication;
import org.hswebframework.web.authorization.Permission;
import org.hswebframework.web.authorization.builder.DataAccessConfigBuilderFactory;
import org.hswebframework.web.authorization.simple.SimpleAuthentication;
import org.hswebframework.web.authorization.simple.SimplePermission;
import org.hswebframework.web.authorization.simple.SimpleRole;
import org.hswebframework.web.authorization.simple.SimpleUser;
import java.util.*;
import java.util.stream.Collectors;
/**
* <pre>
* hsweb:
* users:
* admin:
* name: 超级管理员
* username: admin
* password: admin
* roles:
* - id: admin
* name: 管理员
* - id: user
* name: 用户
* permissions:
* - id: user-manager
* actions: *
* dataAccesses:
* - action: query
* type: DENY_FIELDS
* fields: password,salt
* </pre>
*
* @author zhouhao
* @since 3.0.0-RC
*/
@Getter
@Setter
public class EmbedAuthenticationInfo {
private String id;
private String name;
private String username;
private String type;
private String password;
private List<SimpleRole> roles = new ArrayList<>();
private List<PermissionInfo> permissions = new ArrayList<>();
private Map<String, List<String>> permissionsSimple = new HashMap<>();
@Getter
@Setter
public static class PermissionInfo {
private String id;
private String name;
private Set<String> actions = new HashSet<>();
private List<Map<String, Object>> dataAccesses = new ArrayList<>();
}
public Authentication toAuthentication(DataAccessConfigBuilderFactory factory) {
SimpleAuthentication authentication = new SimpleAuthentication();
SimpleUser user = new SimpleUser();
user.setId(id);
user.setName(name);
user.setUsername(username);
user.setType(type);
authentication.setUser(user);
authentication.setRoles((List) roles);
List<Permission> permissionList = new ArrayList<>();
permissionList.addAll(permissions.stream()
.map(info -> {
SimplePermission permission = new SimplePermission();
permission.setId(info.getId());
permission.setName(info.getName());
permission.setActions(info.getActions());
permission.setDataAccesses(info.getDataAccesses()
.stream().map(conf -> factory.create()
.fromMap(conf)
.build()).collect(Collectors.toSet()));
return permission;
})
.collect(Collectors.toList()));
permissionList.addAll(permissionsSimple.entrySet().stream()
.map(entry -> {
SimplePermission permission = new SimplePermission();
permission.setId(entry.getKey());
permission.setActions(new HashSet<>(entry.getValue()));
return permission;
}).collect(Collectors.toList()));
authentication.setPermissions(permissionList);
return authentication;
}
}

View File

@@ -1,87 +1,37 @@
package org.hswebframework.web.authorization.basic.embed;
import lombok.Getter;
import lombok.Setter;
import org.hswebframework.web.authorization.Authentication;
import org.hswebframework.web.authorization.AuthenticationManager;
import org.hswebframework.web.authorization.AuthenticationRequest;
import org.hswebframework.web.authorization.builder.DataAccessConfigBuilderFactory;
import org.hswebframework.web.authorization.simple.PlainTextUsernamePasswordAuthenticationRequest;
import org.hswebframework.web.authorization.simple.builder.SimpleDataAccessConfigBuilderFactory;
import org.hswebframework.web.authorization.ReactiveAuthenticationManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import javax.validation.ValidationException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
/**
* @author zhouhao
* @since 3.0.0-RC
*/
@ConfigurationProperties(prefix = "hsweb")
@Order(Ordered.HIGHEST_PRECEDENCE)
public class EmbedAuthenticationManager implements AuthenticationManager {
private Map<String, Authentication> authentications = new HashMap<>();
@Autowired(required = false)
private DataAccessConfigBuilderFactory dataAccessConfigBuilderFactory = new SimpleDataAccessConfigBuilderFactory();
@Getter
@Setter
private Map<String, EmbedAuthenticationProperties> users = new HashMap<>();
@PostConstruct
public void init() {
users.forEach((id, properties) -> {
if (StringUtils.isEmpty(properties.getId())) {
properties.setId(id);
}
for (EmbedAuthenticationProperties.PermissionInfo permissionInfo : properties.getPermissions()) {
for (Map<String, Object> objectMap : permissionInfo.getDataAccesses()) {
for (Map.Entry<String, Object> stringObjectEntry : objectMap.entrySet()) {
if (stringObjectEntry.getValue() instanceof Map) {
Map<?, ?> mapVal = ((Map) stringObjectEntry.getValue());
boolean maybeIsList = mapVal.keySet().stream().allMatch(org.hswebframework.utils.StringUtils::isInt);
if (maybeIsList) {
stringObjectEntry.setValue(mapVal.values());
}
}
}
}
}
authentications.put(id, properties.toAuthentication(dataAccessConfigBuilderFactory));
});
}
@Autowired
private EmbedAuthenticationProperties properties;
@Override
public Mono<Authentication> authenticate(Mono<AuthenticationRequest> request) {
return request.filter(r -> r instanceof PlainTextUsernamePasswordAuthenticationRequest)
.map(PlainTextUsernamePasswordAuthenticationRequest.class::cast)
.map(pwdReq -> users.values()
.stream()
.filter(user ->
pwdReq.getUsername().equals(user.getUsername())
&& pwdReq.getPassword().equals(user.getPassword()))
.findFirst()
.map(EmbedAuthenticationProperties::getId)
.map(authentications::get)
.orElseThrow(() -> new ValidationException("用户不存在")));
public Authentication authenticate(AuthenticationRequest request) {
return properties.authenticate(request);
}
@Override
public Mono<Authentication> getByUserId(String userId) {
return Mono.just(authentications.get(userId));
public Optional<Authentication> getByUserId(String userId) {
return properties.getAuthentication(userId);
}
void addAuthentication(Authentication authentication) {
authentications.put(authentication.getUser().getId(), authentication);
}
}

View File

@@ -1,23 +1,27 @@
package org.hswebframework.web.authorization.basic.embed;
import com.alibaba.fastjson.JSON;
import lombok.Getter;
import lombok.Setter;
import org.hswebframework.web.authorization.Authentication;
import org.hswebframework.web.authorization.Permission;
import org.hswebframework.web.authorization.Role;
import org.hswebframework.web.authorization.AuthenticationRequest;
import org.hswebframework.web.authorization.builder.DataAccessConfigBuilderFactory;
import org.hswebframework.web.authorization.simple.SimpleAuthentication;
import org.hswebframework.web.authorization.simple.SimplePermission;
import org.hswebframework.web.authorization.simple.SimpleRole;
import org.hswebframework.web.authorization.simple.SimpleUser;
import org.hswebframework.web.authorization.simple.PlainTextUsernamePasswordAuthenticationRequest;
import org.hswebframework.web.authorization.simple.builder.SimpleDataAccessConfigBuilderFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;
import java.util.*;
import java.util.stream.Collectors;
import javax.validation.ValidationException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
/**
* <pre>
* hsweb:
* auth:
* users:
* admin:
* name: 超级管理员
@@ -42,72 +46,61 @@ import java.util.stream.Collectors;
*/
@Getter
@Setter
public class EmbedAuthenticationProperties {
@ConfigurationProperties(prefix = "hsweb.auth")
public class EmbedAuthenticationProperties implements InitializingBean {
private String id;
private String name;
private String username;
private String type;
private String password;
private List<SimpleRole> roles = new ArrayList<>();
private List<PermissionInfo> permissions = new ArrayList<>();
private Map<String, List<String>> permissionsSimple = new HashMap<>();
private Map<String, Authentication> authentications = new HashMap<>();
@Getter
@Setter
public static class PermissionInfo {
private String id;
private Map<String, EmbedAuthenticationInfo> users = new HashMap<>();
private String name;
@Autowired(required = false)
private DataAccessConfigBuilderFactory dataAccessConfigBuilderFactory = new SimpleDataAccessConfigBuilderFactory();
private Set<String> actions = new HashSet<>();
private List<Map<String, Object>> dataAccesses = new ArrayList<>();
@Override
public void afterPropertiesSet() {
users.forEach((id, properties) -> {
if (StringUtils.isEmpty(properties.getId())) {
properties.setId(id);
}
for (EmbedAuthenticationInfo.PermissionInfo permissionInfo : properties.getPermissions()) {
for (Map<String, Object> objectMap : permissionInfo.getDataAccesses()) {
for (Map.Entry<String, Object> stringObjectEntry : objectMap.entrySet()) {
if (stringObjectEntry.getValue() instanceof Map) {
Map<?, ?> mapVal = ((Map) stringObjectEntry.getValue());
boolean maybeIsList = mapVal.keySet().stream().allMatch(org.hswebframework.utils.StringUtils::isInt);
if (maybeIsList) {
stringObjectEntry.setValue(mapVal.values());
}
}
}
}
}
authentications.put(id, properties.toAuthentication(dataAccessConfigBuilderFactory));
});
}
public Authentication toAuthentication(DataAccessConfigBuilderFactory factory) {
SimpleAuthentication authentication = new SimpleAuthentication();
SimpleUser user = new SimpleUser();
user.setId(id);
user.setName(name);
user.setUsername(username);
user.setType(type);
authentication.setUser(user);
authentication.setRoles((List) roles);
List<Permission> permissionList = new ArrayList<>();
public Authentication authenticate(AuthenticationRequest request) {
if(request instanceof PlainTextUsernamePasswordAuthenticationRequest){
PlainTextUsernamePasswordAuthenticationRequest pwdReq = ((PlainTextUsernamePasswordAuthenticationRequest) request);
return users.values()
.stream()
.filter(user ->
pwdReq.getUsername().equals(user.getUsername())
&& pwdReq.getPassword().equals(user.getPassword()))
.findFirst()
.map(EmbedAuthenticationInfo::getId)
.map(authentications::get)
.orElseThrow(() -> new ValidationException("用户不存在"));
}
permissionList.addAll(permissions.stream()
.map(info -> {
SimplePermission permission = new SimplePermission();
permission.setId(info.getId());
permission.setName(info.getName());
permission.setActions(info.getActions());
permission.setDataAccesses(info.getDataAccesses()
.stream().map(conf -> factory.create()
.fromJson(JSON.toJSONString(conf))
.build()).collect(Collectors.toSet()));
return permission;
})
.collect(Collectors.toList()));
permissionList.addAll(permissionsSimple.entrySet().stream()
.map(entry -> {
SimplePermission permission = new SimplePermission();
permission.setId(entry.getKey());
permission.setActions(new HashSet<>(entry.getValue()));
return permission;
}).collect(Collectors.toList()));
authentication.setPermissions(permissionList);
return authentication;
throw new UnsupportedOperationException("不支持的授权请求:"+request);
}
public Optional<Authentication> getAuthentication(String userId) {
return Optional.ofNullable(authentications.get(userId));
}
}

View File

@@ -0,0 +1,35 @@
package org.hswebframework.web.authorization.basic.embed;
import lombok.AllArgsConstructor;
import org.hswebframework.web.authorization.Authentication;
import org.hswebframework.web.authorization.AuthenticationRequest;
import org.hswebframework.web.authorization.ReactiveAuthenticationManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import reactor.core.publisher.Mono;
/**
* @author zhouhao
* @since 3.0.0-RC
*/
@Order(Ordered.HIGHEST_PRECEDENCE)
@AllArgsConstructor
public class EmbedReactiveAuthenticationManager implements ReactiveAuthenticationManager {
private EmbedAuthenticationProperties properties;
@Override
public Mono<Authentication> authenticate(Mono<AuthenticationRequest> request) {
return request.map(properties::authenticate);
}
@Override
public Mono<Authentication> getByUserId(String userId) {
return Mono.justOrEmpty(properties.getAuthentication(userId));
}
}

View File

@@ -31,7 +31,6 @@ public final class DefaultDataAccessController implements DataAccessController {
}
this.parent = parent;
addHandler(new CustomDataAccessHandler()).
addHandler(new ScriptDataAccessHandler()).
addHandler(new FieldFilterDataAccessHandler()).
addHandler(new FieldScopeDataAccessHandler());
}

View File

@@ -1,41 +0,0 @@
package org.hswebframework.web.authorization.basic.handler.access;
import org.apache.commons.codec.digest.DigestUtils;
import org.hswebframework.expands.script.engine.DynamicScriptEngine;
import org.hswebframework.expands.script.engine.DynamicScriptEngineFactory;
import org.hswebframework.utils.StringUtils;
import org.hswebframework.web.exception.BusinessException;
import org.hswebframework.web.authorization.access.DataAccessConfig;
import org.hswebframework.web.authorization.access.DataAccessHandler;
import org.hswebframework.web.authorization.access.ScriptDataAccessConfig;
import org.hswebframework.web.authorization.define.AuthorizingContext;
/**
* @author zhouhao
*/
public class ScriptDataAccessHandler implements DataAccessHandler {
@Override
public boolean isSupport(DataAccessConfig access) {
return access instanceof ScriptDataAccessConfig;
}
@Override
public boolean handle(DataAccessConfig access, AuthorizingContext context) {
ScriptDataAccessConfig dataAccess = ((ScriptDataAccessConfig) access);
DynamicScriptEngine engine = DynamicScriptEngineFactory.getEngine(dataAccess.getScriptLanguage());
if (engine == null) {
throw new UnsupportedOperationException(dataAccess.getScriptLanguage() + " {not_support}");
}
String scriptId = DigestUtils.md5Hex(dataAccess.getScript());
try {
if (!engine.compiled(scriptId)) {
engine.compile(scriptId, dataAccess.getScript());
}
Object success = engine.execute(scriptId, context.getParamContext().getParams()).getIfSuccess();
return StringUtils.isTrue(success);
} catch (Exception e) {
throw new BusinessException("{script_error}", e);
}
}
}

View File

@@ -23,6 +23,7 @@ import io.swagger.annotations.ApiParam;
import lombok.SneakyThrows;
import org.hswebframework.web.authorization.Authentication;
import org.hswebframework.web.authorization.AuthenticationManager;
import org.hswebframework.web.authorization.ReactiveAuthenticationManager;
import org.hswebframework.web.authorization.annotation.Authorize;
import org.hswebframework.web.authorization.events.*;
import org.hswebframework.web.authorization.simple.PlainTextUsernamePasswordAuthenticationRequest;
@@ -48,7 +49,7 @@ import java.util.function.Function;
public class AuthorizationController {
@Autowired
private AuthenticationManager authenticationManager;
private ReactiveAuthenticationManager authenticationManager;
@Autowired
private ApplicationEventPublisher eventPublisher;

View File

@@ -5,6 +5,7 @@ import io.swagger.annotations.ApiOperation;
import org.hswebframework.web.authorization.Authentication;
import org.hswebframework.web.authorization.AuthenticationManager;
import org.hswebframework.web.authorization.Permission;
import org.hswebframework.web.authorization.ReactiveAuthenticationManager;
import org.hswebframework.web.authorization.annotation.Authorize;
import org.hswebframework.web.authorization.exception.UnAuthorizedException;
import org.hswebframework.web.authorization.token.TokenState;
@@ -22,10 +23,10 @@ import reactor.core.publisher.Mono;
@RequestMapping
@Authorize(permission = "user-token", description = "用户令牌信息管理")
@Api(tags = "权限-用户令牌管理", value = "权限-用户令牌管理")
public class UserTokenController {
public class ReactiveUserTokenController {
private UserTokenManager userTokenManager;
private AuthenticationManager authenticationManager;
private ReactiveAuthenticationManager authenticationManager;
@Autowired
@Lazy
@@ -35,7 +36,7 @@ public class UserTokenController {
@Autowired
@Lazy
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
public void setAuthenticationManager(ReactiveAuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}

View File

@@ -1,43 +1,15 @@
package org.hswebframework.web.authorization.basic.aop;
import org.hswebframework.web.authorization.Authentication;
import org.hswebframework.web.authorization.basic.configuration.EnableAopAuthorize;
import org.hswebframework.web.authorization.basic.web.GeneratedToken;
import org.hswebframework.web.authorization.basic.web.ReactiveUserTokenGenerator;
import org.hswebframework.web.authorization.basic.web.ReactiveUserTokenParser;
import org.hswebframework.web.authorization.token.ParsedToken;
import org.hswebframework.web.id.IDGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext;
import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import reactor.core.publisher.Mono;
import java.util.Collections;
import java.util.Map;
@SpringBootApplication(exclude = {
WebMvcAutoConfiguration.class,
ServletWebServerFactoryAutoConfiguration.class,
DispatcherServletAutoConfiguration.class})
@SpringBootApplication
@EnableAopAuthorize
public class TestApplication {
public static void main(String[] args) {
SpringApplication application=new SpringApplication(TestApplication.class);
application.setApplicationContextClass(ReactiveWebServerApplicationContext.class);
application.run(args);
SpringApplication.run(TestApplication.class,args);
}
}

View File

@@ -1,65 +0,0 @@
package org.hswebframework.web.authorization.basic.aop;
import org.hswebframework.web.authorization.Authentication;
import org.hswebframework.web.authorization.AuthenticationManager;
import org.hswebframework.web.authorization.User;
import org.hswebframework.web.authorization.basic.web.GeneratedToken;
import org.hswebframework.web.authorization.basic.web.ReactiveUserTokenGenerator;
import org.hswebframework.web.authorization.basic.web.ReactiveUserTokenParser;
import org.hswebframework.web.authorization.simple.DefaultAuthorizationAutoConfiguration;
import org.hswebframework.web.authorization.token.ParsedToken;
import org.hswebframework.web.authorization.token.UserTokenManager;
import org.hswebframework.web.id.IDGenerator;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Collections;
import java.util.Map;
@WebFluxTest(FluxTestController.class)
@RunWith(SpringRunner.class)
@Import(DefaultAuthorizationAutoConfiguration.class)
public class WebFluxTests {
@Autowired
private WebTestClient client;
@Autowired
private UserTokenManager tokenManager;
@Test
public void test(){
tokenManager.signIn("test","test-token","admin",10000).block();
client.get().uri("/test")
.header("token","test")
.exchange()
.expectStatus()
.isOk();
}
}

View File

@@ -1,5 +1,6 @@
hsweb:
users:
auth:
users:
admin:
username: admin
password: admin