授权认证重构集成sa-token
parent
c82039a66f
commit
32d35c2db0
|
@ -4,7 +4,7 @@
|
|||
此仓库为奇特物联(iotkit)物联网平台开源项目。
|
||||
奇特物联是一个开源的物联网基础开发平台,提供了物联网及相关业务开发的常见基础功能, 能帮助你快速搭建自己的物联网相关业务平台。
|
||||
|
||||
系统包含了品类、物模型、消息转换、通讯组件(mqtt通讯组件、小度音箱接入组件、onenet Studio接入组件)、云端低代码设备开发、设备管理、规则引擎、第三方平台接入、数据流转、数据可视化、报警中心等模块和智能家居APP(小程序)。
|
||||
系统包含了品类、物模型、消息转换、通讯组件(mqtt通讯组件、小度音箱接入组件、onenet Studio接入组件)、云端低代码设备开发、设备管理、规则引擎、第三方平台接入、数据流转、数据可视化、报警中心等模块和智能家居APP(小程序),集成了[Sa-Token](https://gitee.com/dromara/sa-token) 认证框架。
|
||||
|
||||
**前端项目见:** https://gitee.com/iotkit-open-source/iot-console-web
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
|||
|
||||
#### 软件架构
|
||||
软件架构说明
|
||||
本系统采用springboot、mongodb、redis、elasticsearch、pulsar、keycloak等框架和第三方软件
|
||||
本系统采用springboot、mongodb、redis、elasticsearch、pulsar、sa-token等框架和第三方软件
|
||||
|
||||
|
||||
#### 安装教程
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>iotkit-parent</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ public class ComponentClassLoader {
|
|||
classLoaders.put(name, classLoader);
|
||||
|
||||
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
|
||||
if (!method.isAccessible()) {
|
||||
if (!method.canAccess(classLoader)) {
|
||||
method.setAccessible(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package cc.iotkit.common;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
public interface Constants {
|
||||
|
||||
String PRODUCT_SECRET = "xdkKUymrEGSCYWswqCvSPyRSFvH5j7CU";
|
||||
|
@ -18,6 +16,10 @@ public interface Constants {
|
|||
|
||||
String THING_MODEL_CACHE = "thing_model_cache";
|
||||
|
||||
String USER_CACHE = "user_info_cache";
|
||||
|
||||
String OAUTH_CLIENT_CACHE = "oauth_client_cache";
|
||||
|
||||
String WECHAT_APP_ID = "wx791cb7bf75950e0c";
|
||||
|
||||
String WECHAT_APP_SECRET = "eeef73ce71f1a722ad6298985d859844";
|
||||
|
@ -66,6 +68,11 @@ public interface Constants {
|
|||
*/
|
||||
String HTTP_CONSUMER_DEVICE_INFO_TOPIC = "device_info:";
|
||||
|
||||
/**
|
||||
* 写权限
|
||||
*/
|
||||
String PERMISSION_WRITE = "write";
|
||||
|
||||
/**
|
||||
* 三方平台类型
|
||||
*/
|
||||
|
|
|
@ -96,5 +96,4 @@ public class CodecUtil {
|
|||
encryptStr = new String(HexUtil.parseHex(encryptStr));
|
||||
return StringUtils.isEmpty(encryptStr) ? "" : aesDecryptByBytes(base64Decode(encryptStr), decryptKey);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>iotkit-parent</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package cc.iotkit.dao;
|
||||
|
||||
import cc.iotkit.common.Constants;
|
||||
import cc.iotkit.model.OauthClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@Repository
|
||||
public class OauthClientCache {
|
||||
|
||||
@Autowired
|
||||
private OauthClientRepository oauthClientRepository;
|
||||
|
||||
private static OauthClientCache INSTANCE;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
INSTANCE = this;
|
||||
}
|
||||
|
||||
public static OauthClientCache getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Cacheable(value = Constants.OAUTH_CLIENT_CACHE, key = "#clientId")
|
||||
public OauthClient getClient(String clientId) {
|
||||
return oauthClientRepository.findById(clientId).orElse(null);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package cc.iotkit.dao;
|
||||
|
||||
import cc.iotkit.model.OauthClient;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface OauthClientRepository extends MongoRepository<OauthClient, String> {
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package cc.iotkit.dao;
|
||||
|
||||
import cc.iotkit.common.Constants;
|
||||
import cc.iotkit.model.UserInfo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@Repository
|
||||
public class UserInfoCache {
|
||||
|
||||
@Autowired
|
||||
private UserInfoRepository userInfoRepository;
|
||||
|
||||
private static UserInfoCache INSTANCE;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
INSTANCE = this;
|
||||
}
|
||||
|
||||
public static UserInfoCache getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Cacheable(value = Constants.USER_CACHE, key = "#uid")
|
||||
public UserInfo getUserInfo(String uid) {
|
||||
return userInfoRepository.findById(uid).orElse(null);
|
||||
}
|
||||
|
||||
}
|
|
@ -9,6 +9,8 @@ import java.util.List;
|
|||
@Repository
|
||||
public interface UserInfoRepository extends MongoRepository<UserInfo, String> {
|
||||
|
||||
UserInfo findByUid(String uid);
|
||||
|
||||
List<UserInfo> findByType(int type);
|
||||
|
||||
List<UserInfo> findByTypeAndOwnerId(int type, String ownerId);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>iotkit-parent</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -28,39 +28,20 @@
|
|||
<artifactId>spring-boot-starter-cache</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-to-slf4j</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-spring-security-adapter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-admin-client</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.ws.rs</groupId>
|
||||
<artifactId>javax.ws.rs-api</artifactId>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@ -68,11 +49,6 @@
|
|||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
|
@ -114,6 +90,11 @@
|
|||
<artifactId>joda-time</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>it.ozimov</groupId>
|
||||
<artifactId>embedded-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<artifactId>model</artifactId>
|
||||
|
@ -144,24 +125,29 @@
|
|||
<artifactId>converter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<artifactId>oauth2-server</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<!-- <build>-->
|
||||
<!-- <plugins>-->
|
||||
<!-- <plugin>-->
|
||||
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||
<!-- <artifactId>spring-boot-maven-plugin</artifactId>-->
|
||||
<!-- <configuration>-->
|
||||
<!-- <excludes>-->
|
||||
<!-- <exclude>-->
|
||||
<!-- <groupId>org.projectlombok</groupId>-->
|
||||
<!-- <artifactId>lombok</artifactId>-->
|
||||
<!-- </exclude>-->
|
||||
<!-- </excludes>-->
|
||||
<!-- </configuration>-->
|
||||
<!-- </plugin>-->
|
||||
<!-- </plugins>-->
|
||||
<!-- </build>-->
|
||||
<!-- <build>-->
|
||||
<!-- <plugins>-->
|
||||
<!-- <plugin>-->
|
||||
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||
<!-- <artifactId>spring-boot-maven-plugin</artifactId>-->
|
||||
<!-- <configuration>-->
|
||||
<!-- <excludes>-->
|
||||
<!-- <exclude>-->
|
||||
<!-- <groupId>org.projectlombok</groupId>-->
|
||||
<!-- <artifactId>lombok</artifactId>-->
|
||||
<!-- </exclude>-->
|
||||
<!-- </excludes>-->
|
||||
<!-- </configuration>-->
|
||||
<!-- </plugin>-->
|
||||
<!-- </plugins>-->
|
||||
<!-- </build>-->
|
||||
|
||||
|
||||
<build>
|
||||
|
|
|
@ -1,16 +1,25 @@
|
|||
package cc.iotkit.manager;
|
||||
|
||||
import cc.iotkit.manager.config.EmbeddedElasticSearchConfig;
|
||||
import cc.iotkit.manager.config.EmbeddedRedisConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
@Slf4j
|
||||
@EnableFeignClients(basePackages = {"cc.iotkit.deviceapi"})
|
||||
@SpringBootApplication(scanBasePackages = {"cc.iotkit"})
|
||||
@EnableWebMvc
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (EmbeddedElasticSearchConfig.embeddedEnable()) {
|
||||
EmbeddedElasticSearchConfig.startEmbeddedElasticSearch();
|
||||
}
|
||||
if (EmbeddedRedisConfig.embeddedEnable()) {
|
||||
EmbeddedRedisConfig.startEmbeddedRedisServer();
|
||||
}
|
||||
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,16 @@ public class CacheConfig {
|
|||
Caffeine.newBuilder()
|
||||
.expireAfterWrite(5, TimeUnit.MINUTES)
|
||||
.build()
|
||||
), new CaffeineCache(
|
||||
Constants.USER_CACHE,
|
||||
Caffeine.newBuilder()
|
||||
.expireAfterWrite(5, TimeUnit.MINUTES)
|
||||
.build()
|
||||
), new CaffeineCache(
|
||||
Constants.OAUTH_CLIENT_CACHE,
|
||||
Caffeine.newBuilder()
|
||||
.expireAfterWrite(5, TimeUnit.MINUTES)
|
||||
.build()
|
||||
)
|
||||
));
|
||||
return manager;
|
||||
|
|
|
@ -1,43 +1,31 @@
|
|||
package cc.iotkit.manager.config;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.node.InternalSettingsPreparer;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.elasticsearch.transport.Netty4Plugin;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class ElasticSearchConfig {
|
||||
public class EmbeddedElasticSearchConfig {
|
||||
|
||||
static {
|
||||
System.setProperty("es.set.netty.runtime.available.processors", "false");
|
||||
}
|
||||
|
||||
public static boolean embeddedEnable() {
|
||||
return "true".equals(System.getProperty("embeddedElasticSearch"));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Bean
|
||||
public EmbeddedElasticSearch getEmbeddedElasticSearch(ConfigProperty configProperty) {
|
||||
if (configProperty.enabled) {
|
||||
EmbeddedElasticSearch embeddedElasticSearch = new EmbeddedElasticSearch(configProperty);
|
||||
public static void startEmbeddedElasticSearch() {
|
||||
EmbeddedElasticSearch embeddedElasticSearch = new EmbeddedElasticSearch(new ConfigProperty());
|
||||
embeddedElasticSearch.start();
|
||||
return embeddedElasticSearch;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "elasticsearch.embedded")
|
||||
public static class ConfigProperty {
|
||||
|
||||
private boolean enabled;
|
||||
|
||||
private String dataPath = "./data/elasticsearch";
|
||||
|
||||
private String homePath = "./";
|
|
@ -0,0 +1,16 @@
|
|||
package cc.iotkit.manager.config;
|
||||
|
||||
import redis.embedded.RedisServer;
|
||||
|
||||
public class EmbeddedRedisConfig {
|
||||
|
||||
public static boolean embeddedEnable() {
|
||||
return "true".equals(System.getProperty("embeddedRedisServer"));
|
||||
}
|
||||
|
||||
public static void startEmbeddedRedisServer() {
|
||||
RedisServer redisServer = new RedisServer();
|
||||
redisServer.start();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package cc.iotkit.manager.config;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
@ -18,7 +19,12 @@ public class GlobalExceptionHandler {
|
|||
@ResponseBody
|
||||
public RequestResult handleException(Exception e, HttpServletResponse response) {
|
||||
log.error("handler exception", e);
|
||||
if(e.getMessage().contains("Unauthorized")){
|
||||
if (e instanceof NotLoginException) {
|
||||
response.setStatus(401);
|
||||
return new RequestResult("401", "未授权的请求");
|
||||
}
|
||||
|
||||
if (e.getMessage().contains("Unauthorized")) {
|
||||
response.setStatus(403);
|
||||
return new RequestResult("403", "没有权限");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package cc.iotkit.manager.config;
|
||||
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
@ -34,6 +35,9 @@ public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
|
|||
GlobalExceptionHandler.RequestResult requestResult = (GlobalExceptionHandler.RequestResult) body;
|
||||
return new ApiResponse(Integer.parseInt(requestResult.getCode()), requestResult.getMessage(),
|
||||
"", System.currentTimeMillis());
|
||||
} else if (body instanceof SaResult) {
|
||||
SaResult result = (SaResult) body;
|
||||
return new ApiResponse(result.getCode(), result.getMsg(), result.getData(), System.currentTimeMillis());
|
||||
} else if (body instanceof Map) {
|
||||
Map map = (Map) body;
|
||||
//spring mvc内部异常
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package cc.iotkit.manager.config;
|
||||
|
||||
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
|
||||
import cn.dev33.satoken.interceptor.SaRouteInterceptor;
|
||||
import cn.dev33.satoken.router.SaRouter;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class SaTokenConfigure implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册注解拦截器
|
||||
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
|
||||
// 注册路由拦截器,自定义认证规则
|
||||
registry.addInterceptor(new SaRouteInterceptor((req, res, handler) -> {
|
||||
System.out.println(req.getRequestPath());
|
||||
// 根据路由划分模块,不同模块不同鉴权
|
||||
SaRouter
|
||||
//管理员、系统用户角色能使用的功能
|
||||
.match("/**")
|
||||
.notMatch("/oauth2/**","/*.png").check(c -> StpUtil.checkRoleOr("iot_admin", "iot_system"))
|
||||
//需要有可写权限的功能
|
||||
.match(
|
||||
"/**/save*/**",
|
||||
"/**/remove*/**",
|
||||
"/**/del*/**",
|
||||
"/**/add*/**",
|
||||
"/**/clear*/**",
|
||||
"/**/set*/**",
|
||||
"/**/set",
|
||||
"/**/invoke"
|
||||
).check(c -> StpUtil.checkPermission("write"))
|
||||
//管理员、系统、客户端用户角色能使用的功能
|
||||
.match("/space/addSpace/**",
|
||||
"/space/saveSpace/**",
|
||||
"/space/delSpace/**",
|
||||
"/space/saveHome/**",
|
||||
"/space/currentHome/**",
|
||||
"/space/myRecentDevices/**",
|
||||
"/space/spaces/**",
|
||||
"/space/myDevices/**",
|
||||
"/space/findDevice/**",
|
||||
"/space/addDevice/**",
|
||||
"/space/saveDevice",
|
||||
"/space/removeDevice",
|
||||
"/space/device/*",
|
||||
"/device/*/consumer/*",
|
||||
"/device/*/service/property/set",
|
||||
"/device/*/service/*/invoke"
|
||||
)
|
||||
.check(c -> StpUtil.checkRoleOr("iot_admin", "iot_system", "iot_client"))
|
||||
;
|
||||
})).addPathPatterns("/**")
|
||||
.excludePathPatterns(
|
||||
"/*.png",
|
||||
"/oauth2/**", "/*.html",
|
||||
"/favicon.ico", "/v2/api-docs",
|
||||
"/webjars/**", "/swagger-resources/**",
|
||||
"/*.js");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package cc.iotkit.manager.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
|
||||
@Configuration
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Value("${app.systemRole}")
|
||||
private String systemRole;
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// super.configure(http);
|
||||
http
|
||||
.authorizeRequests()
|
||||
.antMatchers("/oauth2/**", "/*.html", "/favicon.ico", "/v2/api-docs", "/webjars/**", "/swagger-resources/**", "/*.js").permitAll()
|
||||
.antMatchers("/api/**").hasRole("iot_client_user")
|
||||
.antMatchers("/aligenieDevice/invoke/**").hasRole("iot_client_user")
|
||||
//客户端用户写权限
|
||||
.antMatchers("/space/addSpace/**").hasAnyRole("iot_write", "iot_client_user")
|
||||
.antMatchers("/space/saveSpace/**").hasAnyRole("iot_write", "iot_client_user")
|
||||
.antMatchers("/space/delSpace/**").hasAnyRole("iot_write", "iot_client_user")
|
||||
.antMatchers("/space/saveHome/**").hasAnyRole("iot_write", "iot_client_user")
|
||||
.antMatchers("/space/currentHome/**").hasAnyRole("iot_write", "iot_client_user")
|
||||
.antMatchers("/space/myRecentDevices/**").hasAnyRole("iot_write", "iot_client_user")
|
||||
.antMatchers("/space/spaces/**").hasAnyRole("iot_write", "iot_client_user")
|
||||
.antMatchers("/space/myDevices/**").hasAnyRole("iot_write", "iot_client_user")
|
||||
.antMatchers("/space/findDevice/**").hasAnyRole("iot_write", "iot_client_user")
|
||||
.antMatchers("/space/addDevice/**").hasAnyRole("iot_write", "iot_client_user")
|
||||
.antMatchers("/space/saveDevice").hasAnyRole("iot_write", "iot_client_user")
|
||||
.antMatchers("/space/removeDevice").hasAnyRole("iot_write", "iot_client_user")
|
||||
.antMatchers("/space/device/*").hasAnyRole("iot_write", "iot_client_user")
|
||||
.antMatchers("/device/*/consumer/*").hasAnyRole("iot_write", "iot_client_user")
|
||||
.antMatchers("/device/*/service/property/set").hasAnyRole("iot_write", "iot_client_user")
|
||||
.antMatchers("/device/*/service/*/invoke").hasAnyRole("iot_write", "iot_client_user")
|
||||
|
||||
|
||||
.antMatchers(HttpMethod.DELETE).hasRole("iot_write")
|
||||
.antMatchers(HttpMethod.PUT).hasRole("iot_write")
|
||||
.antMatchers("/**/save*/**").hasRole("iot_write")
|
||||
.antMatchers("/**/remove*/**").hasRole("iot_write")
|
||||
.antMatchers("/**/del*/**").hasRole("iot_write")
|
||||
.antMatchers("/**/add*/**").hasRole("iot_write")
|
||||
.antMatchers("/**/clear*/**").hasRole("iot_write")
|
||||
.antMatchers("/**/set*/**").hasRole("iot_write")
|
||||
.antMatchers("/**/set").hasRole("iot_write")
|
||||
.antMatchers("/**/invoke").hasRole("iot_write")
|
||||
.antMatchers("/**").hasAnyRole(systemRole)
|
||||
.and().csrf().disable();
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import cc.iotkit.model.product.Category;
|
|||
import cc.iotkit.model.product.Product;
|
||||
import cc.iotkit.model.product.ProductModel;
|
||||
import cc.iotkit.model.product.ThingModel;
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import com.aliyun.oss.OSS;
|
||||
import com.aliyun.oss.OSSClientBuilder;
|
||||
import com.aliyun.oss.model.PutObjectResult;
|
||||
|
@ -23,7 +24,6 @@ import org.springframework.data.domain.Example;
|
|||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
|
@ -101,14 +101,14 @@ public class ProductController {
|
|||
return categoryRepository.findAll();
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('iot_admin')")
|
||||
@SaCheckRole("iot_admin")
|
||||
@PostMapping("/saveCategory")
|
||||
public void saveCategory(Category cate) {
|
||||
cate.setCreateAt(System.currentTimeMillis());
|
||||
categoryRepository.save(cate);
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('iot_admin')")
|
||||
@SaCheckRole("iot_admin")
|
||||
@PostMapping("/delCategory")
|
||||
public void delCategory(String id) {
|
||||
categoryRepository.deleteById(id);
|
||||
|
|
|
@ -16,7 +16,6 @@ import cc.iotkit.model.space.SpaceDevice;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Example;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -103,7 +102,6 @@ public class SpaceDeviceController {
|
|||
.build();
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('iot_system_user')")
|
||||
@GetMapping("/{userId}/devices")
|
||||
public List<SpaceDeviceVo> getDevices(@PathVariable("userId") String userId) {
|
||||
List<SpaceDevice> spaceDevices = spaceDeviceRepository.findAll(Example.of(SpaceDevice.builder().uid(userId).build()));
|
||||
|
|
0
manager/src/main/java/cc/iotkit/manager/controller/ThirdAuthController.java
Normal file → Executable file
0
manager/src/main/java/cc/iotkit/manager/controller/ThirdAuthController.java
Normal file → Executable file
|
@ -2,17 +2,17 @@ package cc.iotkit.manager.controller;
|
|||
|
||||
import cc.iotkit.common.Constants;
|
||||
import cc.iotkit.common.exception.BizException;
|
||||
import cc.iotkit.common.utils.CodecUtil;
|
||||
import cc.iotkit.common.utils.ReflectUtil;
|
||||
import cc.iotkit.dao.AligenieDeviceRepository;
|
||||
import cc.iotkit.dao.UserInfoRepository;
|
||||
import cc.iotkit.manager.service.DataOwnerService;
|
||||
import cc.iotkit.manager.service.KeycloakAdminService;
|
||||
import cc.iotkit.manager.service.PulsarAdminService;
|
||||
import cc.iotkit.manager.utils.AuthUtil;
|
||||
import cc.iotkit.model.UserInfo;
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.*;
|
||||
|
@ -24,8 +24,6 @@ public class UserInfoController {
|
|||
@Value("${app.systemRole}")
|
||||
private String systemRole;
|
||||
|
||||
@Autowired
|
||||
private KeycloakAdminService keycloakAdminService;
|
||||
@Autowired
|
||||
private UserInfoRepository userInfoRepository;
|
||||
@Autowired
|
||||
|
@ -39,7 +37,7 @@ public class UserInfoController {
|
|||
/**
|
||||
* 平台用户列表
|
||||
*/
|
||||
@PreAuthorize("hasRole('iot_admin')")
|
||||
@SaCheckRole("iot_admin")
|
||||
@GetMapping("/platform/users")
|
||||
public List<UserInfo> getPlatformUsers() {
|
||||
return userInfoRepository.findByType(UserInfo.USER_TYPE_PLATFORM);
|
||||
|
@ -54,18 +52,10 @@ public class UserInfoController {
|
|||
user.setId(UUID.randomUUID().toString());
|
||||
user.setType(UserInfo.USER_TYPE_PLATFORM);
|
||||
user.setOwnerId(AuthUtil.getUserId());
|
||||
user.setRoles(Arrays.asList(Constants.ROLE_SYSTEM));
|
||||
user.setRoles(Collections.singletonList(Constants.ROLE_SYSTEM));
|
||||
user.setPermissions(Collections.singletonList(Constants.PERMISSION_WRITE));
|
||||
user.setCreateAt(System.currentTimeMillis());
|
||||
UserInfo keycloakUser = keycloakAdminService.getUser(user.getUid());
|
||||
if (keycloakUser != null) {
|
||||
user.setId(keycloakUser.getId());
|
||||
keycloakAdminService.updateUser(user);
|
||||
} else {
|
||||
keycloakAdminService.createUser(user, Constants.PWD_SYSTEM_USER);
|
||||
}
|
||||
if (!pulsarAdminService.tenantExists(user.getUid())) {
|
||||
pulsarAdminService.createTenant(user.getUid());
|
||||
}
|
||||
user.setSecret(CodecUtil.aesEncrypt(Constants.PWD_SYSTEM_USER, Constants.PWD_SYSTEM_USER));
|
||||
userInfoRepository.save(user);
|
||||
} catch (Throwable e) {
|
||||
throw new BizException("add platform user error", e);
|
||||
|
@ -84,25 +74,23 @@ public class UserInfoController {
|
|||
* 添加C端用户
|
||||
*/
|
||||
@PostMapping("/client/user/add")
|
||||
public void addClientUser(@RequestBody UserInfo user) {
|
||||
public void addClientUser(@RequestBody UserInfo user) throws Exception {
|
||||
user.setType(UserInfo.USER_TYPE_CLIENT);
|
||||
user.setOwnerId(AuthUtil.getUserId());
|
||||
user.setRoles(Collections.singletonList(Constants.ROLE_CLIENT));
|
||||
user.setCreateAt(System.currentTimeMillis());
|
||||
String uid = keycloakAdminService.createUser(user, Constants.PWD_CLIENT_USER);
|
||||
user.setId(uid);
|
||||
user.setSecret(CodecUtil.aesEncrypt(Constants.PWD_CLIENT_USER, Constants.ACCOUNT_SECRET));
|
||||
userInfoRepository.save(user);
|
||||
}
|
||||
|
||||
@PostMapping("/client/user/{id}/delete")
|
||||
public void deleteClientUser(@PathVariable("id") String id) {
|
||||
Optional<UserInfo> optUser = userInfoRepository.findById(id);
|
||||
if (!optUser.isPresent()) {
|
||||
if (optUser.isEmpty()) {
|
||||
throw new BizException("user does not exist");
|
||||
}
|
||||
UserInfo user = optUser.get();
|
||||
ownerService.checkOwner(user);
|
||||
keycloakAdminService.deleteUser(id);
|
||||
userInfoRepository.deleteById(id);
|
||||
aligenieDeviceRepository.deleteByUid(user.getId());
|
||||
}
|
||||
|
@ -110,7 +98,7 @@ public class UserInfoController {
|
|||
@PostMapping("/client/user/save")
|
||||
public void saveClientUser(@RequestBody UserInfo user) {
|
||||
Optional<UserInfo> userOpt = userInfoRepository.findById(user.getId());
|
||||
if (!userOpt.isPresent()) {
|
||||
if (userOpt.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
UserInfo oldUser = userOpt.get();
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package cc.iotkit.manager.controller.aligenie;
|
||||
|
||||
import cc.iotkit.common.Constants;
|
||||
import cc.iotkit.common.exception.BizException;
|
||||
import cc.iotkit.common.utils.UniqueIdUtil;
|
||||
import cc.iotkit.dao.*;
|
||||
import cc.iotkit.manager.service.DataOwnerService;
|
||||
import cc.iotkit.manager.service.DeviceService;
|
||||
|
@ -10,21 +8,13 @@ import cc.iotkit.model.UserInfo;
|
|||
import cc.iotkit.model.aligenie.AligenieDevice;
|
||||
import cc.iotkit.model.aligenie.AligenieProduct;
|
||||
import cc.iotkit.model.device.DeviceInfo;
|
||||
import cc.iotkit.model.device.message.ThingModelMessage;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.pulsar.client.api.Producer;
|
||||
import org.apache.pulsar.client.api.PulsarClient;
|
||||
import org.apache.pulsar.client.api.PulsarClientException;
|
||||
import org.apache.pulsar.client.impl.schema.JSONSchema;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
|
@ -46,24 +36,9 @@ public class AligenieDeviceController {
|
|||
private DeviceService deviceService;
|
||||
@Autowired
|
||||
private DeviceDao deviceDao;
|
||||
@Value("${app.aligenie.push.device}")
|
||||
private String pushDevice;
|
||||
@Value("${pulsar.broker}")
|
||||
private String pulsarBrokerUrl;
|
||||
|
||||
private Producer<ThingModelMessage> deviceMessageProducer;
|
||||
|
||||
@PostConstruct
|
||||
public void init() throws PulsarClientException {
|
||||
//初始化pulsar客户端
|
||||
PulsarClient client = PulsarClient.builder()
|
||||
.serviceUrl(pulsarBrokerUrl)
|
||||
.build();
|
||||
deviceMessageProducer = client.newProducer(JSONSchema.of(ThingModelMessage.class))
|
||||
.topic("persistent://iotkit/default/" + Constants.THING_MODEL_MESSAGE_TOPIC)
|
||||
.create();
|
||||
}
|
||||
|
||||
@GetMapping("/list/{uid}")
|
||||
public List<AligenieDevice> getDevices(@PathVariable("uid") String uid) {
|
||||
UserInfo user = userInfoRepository.findById(uid).get();
|
||||
|
@ -73,9 +48,9 @@ public class AligenieDeviceController {
|
|||
|
||||
@PostMapping("/bind/{uid}")
|
||||
public void bind(@PathVariable("uid") String uid,
|
||||
@RequestBody List<Device> devices) throws PulsarClientException {
|
||||
@RequestBody List<Device> devices) {
|
||||
Optional<UserInfo> optUser = userInfoRepository.findById(uid);
|
||||
if (!optUser.isPresent()) {
|
||||
if (optUser.isEmpty()) {
|
||||
throw new BizException("user does not exist");
|
||||
}
|
||||
UserInfo user = optUser.get();
|
||||
|
@ -106,23 +81,6 @@ public class AligenieDeviceController {
|
|||
deviceDao.updateTag(device.getDeviceId(),
|
||||
new DeviceInfo.Tag("aligenie", "天猫精灵接入", "是"));
|
||||
}
|
||||
|
||||
DeviceInfo deviceInfo = deviceRepository.findByDeviceId(pushDevice);
|
||||
if (deviceInfo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> uidData = new HashMap<>();
|
||||
uidData.put("uid", uid);
|
||||
deviceMessageProducer.send(ThingModelMessage.builder()
|
||||
.deviceId(pushDevice)
|
||||
.productKey(deviceInfo.getProductKey())
|
||||
.deviceName(deviceInfo.getDeviceName())
|
||||
.type(ThingModelMessage.TYPE_EVENT)
|
||||
.identifier("userDevicesChange")
|
||||
.mid(UniqueIdUtil.newRequestId())
|
||||
.data(uidData)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Data
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
package cc.iotkit.manager.controller.aligenie;
|
||||
|
||||
import cc.iotkit.dao.AligenieProductRepository;
|
||||
import cc.iotkit.manager.model.aligenie.AligenieProductVo;
|
||||
import cc.iotkit.manager.service.DataOwnerService;
|
||||
import cc.iotkit.manager.utils.AuthUtil;
|
||||
import cc.iotkit.model.aligenie.AligenieProduct;
|
||||
import cc.iotkit.model.product.Product;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Example;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
|
|
|
@ -1,33 +1,19 @@
|
|||
package cc.iotkit.manager.utils;
|
||||
|
||||
import cc.iotkit.common.Constants;
|
||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AuthUtil {
|
||||
|
||||
public static String getUserId() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication == null) {
|
||||
return "";
|
||||
}
|
||||
if (authentication instanceof KeycloakAuthenticationToken) {
|
||||
KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) authentication;
|
||||
return token.getName();
|
||||
}
|
||||
return "";
|
||||
return StpUtil.getLoginId().toString();
|
||||
}
|
||||
|
||||
public static List<String> getUserRoles() {
|
||||
return SecurityContextHolder.getContext()
|
||||
.getAuthentication()
|
||||
.getAuthorities()
|
||||
.stream().map((role) -> role.getAuthority().replace("ROLE_", ""))
|
||||
.collect(Collectors.toList());
|
||||
return StpUtil.getRoleList();
|
||||
}
|
||||
|
||||
public static boolean isAdmin() {
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
server:
|
||||
port: 8086
|
||||
|
||||
spring:
|
||||
servlet:
|
||||
multipart:
|
||||
|
@ -36,28 +39,21 @@ aliyun:
|
|||
accessKeyId: 填写阿里云accessKeyId
|
||||
accessKeySecret: 填写阿里云accessKeySecret
|
||||
|
||||
keycloak:
|
||||
realm : 填写keycloak中定义的realm
|
||||
resource : iotkit
|
||||
auth-server-url : 填写keycloak认证地址
|
||||
ssl-required : external
|
||||
use-resource-role-mappings : false
|
||||
cors-max-age : 1000
|
||||
cors-allowed-methods : POST PUT DELETE GET
|
||||
cors-exposed-headers : WWW-Authenticate
|
||||
bearer-only : false
|
||||
enable-basic-auth : false
|
||||
expose-token : true
|
||||
verify-token-audience : false
|
||||
connection-pool-size : 20
|
||||
disable-trust-manager: true
|
||||
allow-any-hostname : false
|
||||
token-minimum-time-to-live : 10
|
||||
min-time-between-jwks-requests : 10
|
||||
|
||||
keycloak-admin-clientid : 填写keycloak中定义的clientId
|
||||
keycloak-admin-user : 填写keycloak中添加的管理员用户名
|
||||
keycloak-admin-password : 填写keycloak中添加的管理员密码
|
||||
sa-token:
|
||||
# token名称 (同时也是cookie名称)
|
||||
token-name: satoken
|
||||
# token有效期,单位s 默认30天, -1代表永不过期
|
||||
timeout: 2592000
|
||||
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
|
||||
activity-timeout: -1
|
||||
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||
is-concurrent: true
|
||||
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||
is-share: false
|
||||
# token风格
|
||||
token-style: uuid
|
||||
# 是否输出操作日志
|
||||
is-log: false
|
||||
|
||||
pulsar:
|
||||
broker: pulsar://pulsar broker地址:6650
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
server:
|
||||
port: 8086
|
||||
|
||||
spring:
|
||||
servlet:
|
||||
multipart:
|
||||
|
@ -34,28 +37,21 @@ aliyun:
|
|||
accessKeyId: 填写阿里云accessKeyId
|
||||
accessKeySecret: 填写阿里云accessKeySecret
|
||||
|
||||
keycloak:
|
||||
realm : 填写keycloak中定义的realm
|
||||
resource : iotkit
|
||||
auth-server-url : 填写keycloak认证地址
|
||||
ssl-required : external
|
||||
use-resource-role-mappings : false
|
||||
cors-max-age : 1000
|
||||
cors-allowed-methods : POST PUT DELETE GET
|
||||
cors-exposed-headers : WWW-Authenticate
|
||||
bearer-only : false
|
||||
enable-basic-auth : false
|
||||
expose-token : true
|
||||
verify-token-audience : false
|
||||
connection-pool-size : 20
|
||||
disable-trust-manager: true
|
||||
allow-any-hostname : false
|
||||
token-minimum-time-to-live : 10
|
||||
min-time-between-jwks-requests : 10
|
||||
|
||||
keycloak-admin-clientid : 填写keycloak中定义的clientId
|
||||
keycloak-admin-user : 填写keycloak中添加的管理员用户名
|
||||
keycloak-admin-password : 填写keycloak中添加的管理员密码
|
||||
sa-token:
|
||||
# token名称 (同时也是cookie名称)
|
||||
token-name: satoken
|
||||
# token有效期,单位s 默认30天, -1代表永不过期
|
||||
timeout: 2592000
|
||||
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
|
||||
activity-timeout: -1
|
||||
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||
is-concurrent: true
|
||||
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||
is-share: false
|
||||
# token风格
|
||||
token-style: uuid
|
||||
# 是否输出操作日志
|
||||
is-log: false
|
||||
|
||||
pulsar:
|
||||
broker: pulsar://pulsar broker地址:6650
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
<parent>
|
||||
<artifactId>iotkit-parent</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>model</artifactId>
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package cc.iotkit.model;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
/**
|
||||
* oauth2的client
|
||||
*/
|
||||
@Data
|
||||
@Document
|
||||
public class OauthClient {
|
||||
|
||||
@Id
|
||||
private String clientId;
|
||||
|
||||
private String name;
|
||||
|
||||
private String clientSecret;
|
||||
|
||||
private String allowUrl;
|
||||
|
||||
private Long createAt;
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package cc.iotkit.model;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
@Data
|
||||
@Document
|
||||
public class Role {
|
||||
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
}
|
|
@ -35,6 +35,11 @@ public class UserInfo implements Owned {
|
|||
*/
|
||||
private String ownerId;
|
||||
|
||||
/**
|
||||
* 密钥(密码加密后的内容)
|
||||
*/
|
||||
private String secret;
|
||||
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
|
@ -69,7 +74,12 @@ public class UserInfo implements Owned {
|
|||
/**
|
||||
* 角色
|
||||
*/
|
||||
private List<String> roles;
|
||||
private List<String> roles = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
private List<String> permissions = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 用户使用的平台
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>iotkit-parent</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>oauth2-server</artifactId>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-oauth2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.ejlchina</groupId>
|
||||
<artifactId>okhttps</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<artifactId>dao</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
131
oauth2-server/src/main/java/cc/iotkit/oauth/controller/AuthClientController.java
Executable file
131
oauth2-server/src/main/java/cc/iotkit/oauth/controller/AuthClientController.java
Executable file
|
@ -0,0 +1,131 @@
|
|||
package cc.iotkit.oauth.controller;
|
||||
|
||||
import cc.iotkit.common.Constants;
|
||||
import cc.iotkit.common.utils.CodecUtil;
|
||||
import cc.iotkit.dao.OauthClientCache;
|
||||
import cc.iotkit.dao.UserInfoCache;
|
||||
import cc.iotkit.model.OauthClient;
|
||||
import cc.iotkit.model.UserInfo;
|
||||
import cc.iotkit.utils.SoMap;
|
||||
import cn.dev33.satoken.stp.SaLoginConfig;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import com.ejlchina.okhttps.OkHttps;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
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.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/oauth2")
|
||||
public class AuthClientController {
|
||||
|
||||
@Value("${oauth2.auth-server-url}")
|
||||
private String serverUrl;
|
||||
|
||||
@Autowired
|
||||
private OauthClientCache oauthClientCache;
|
||||
@Autowired
|
||||
private UserInfoCache userInfoCache;
|
||||
|
||||
// 进入首页
|
||||
@RequestMapping("/")
|
||||
public Object index(HttpServletRequest request) {
|
||||
request.setAttribute("uid", StpUtil.getLoginIdDefaultNull());
|
||||
return new ModelAndView("index.html");
|
||||
}
|
||||
|
||||
// 根据Code码进行登录,获取 Access-Token 和 openid
|
||||
@RequestMapping("/codeLogin")
|
||||
public SaResult codeLogin(String code, String clientId) {
|
||||
OauthClient oauthClient = oauthClientCache.getClient(clientId);
|
||||
if (oauthClient == null) {
|
||||
return SaResult.error("clientId does not exist");
|
||||
}
|
||||
String clientSecret = oauthClient.getClientSecret();
|
||||
|
||||
// 调用Server端接口,获取 Access-Token 以及其他信息
|
||||
String str = OkHttps.sync(serverUrl + "/oauth2/token")
|
||||
.addBodyPara("grant_type", "authorization_code")
|
||||
.addBodyPara("code", code)
|
||||
.addBodyPara("client_id", clientId)
|
||||
.addBodyPara("client_secret", clientSecret)
|
||||
.post()
|
||||
.getBody()
|
||||
.toString();
|
||||
SoMap so = SoMap.getSoMap().setJsonString(str);
|
||||
log.info("get token by code result:{}", so);
|
||||
// code不等于200 代表请求失败
|
||||
if (so.getInt("code") != 200) {
|
||||
return SaResult.error(so.getString("msg"));
|
||||
}
|
||||
|
||||
// 根据openid获取其对应的userId
|
||||
SoMap data = so.getMap("data");
|
||||
String uid = getUserIdByOpenid(data.getString("openid"));
|
||||
String access_token = data.getString("access_token");
|
||||
UserInfo userInfo = userInfoCache.getUserInfo(uid);
|
||||
data.put("name", userInfo.getNickName());
|
||||
data.put("uid", uid);
|
||||
|
||||
// 返回相关参数
|
||||
StpUtil.login(uid, SaLoginConfig.setToken(access_token));
|
||||
return SaResult.data(data);
|
||||
}
|
||||
|
||||
// 注销登录
|
||||
@RequestMapping("/logout")
|
||||
public RedirectView logout(String accessToken, String redirect_uri) {
|
||||
//先注销client中cookie的token
|
||||
StpUtil.logout();
|
||||
//再注销web页面使用的token
|
||||
StpUtil.logoutByTokenValue(accessToken);
|
||||
|
||||
return new RedirectView(redirect_uri);
|
||||
}
|
||||
|
||||
// 根据 Access-Token 置换相关的资源: 获取账号昵称、头像、性别等信息
|
||||
@RequestMapping("/getUserinfo")
|
||||
public SaResult getUserinfo(String accessToken) {
|
||||
// 调用Server端接口,查询开放的资源
|
||||
String str = OkHttps.sync(serverUrl + "/oauth2/userinfo")
|
||||
.addBodyPara("access_token", accessToken)
|
||||
.post()
|
||||
.getBody()
|
||||
.toString();
|
||||
SoMap so = SoMap.getSoMap().setJsonString(str);
|
||||
// code不等于200 代表请求失败
|
||||
if (so.getInt("code") != 200) {
|
||||
return SaResult.error(so.getString("msg"));
|
||||
}
|
||||
|
||||
// 返回相关参数 (data=获取到的资源 )
|
||||
SoMap data = so.getMap("data");
|
||||
return SaResult.data(data);
|
||||
}
|
||||
|
||||
@GetMapping("/checkLogin")
|
||||
public SaResult checkLogin() {
|
||||
try {
|
||||
StpUtil.checkLogin();
|
||||
} catch (Throwable e) {
|
||||
return SaResult.error("no login");
|
||||
}
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private String getUserIdByOpenid(String openid) {
|
||||
String clientIdLoginId = CodecUtil.aesDecrypt(openid, Constants.ACCOUNT_SECRET);
|
||||
return clientIdLoginId.split(":")[1];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package cc.iotkit.oauth.controller;
|
||||
|
||||
import cc.iotkit.common.Constants;
|
||||
import cc.iotkit.common.utils.CodecUtil;
|
||||
import cc.iotkit.dao.UserInfoRepository;
|
||||
import cc.iotkit.model.UserInfo;
|
||||
import cc.iotkit.oauth.service.TokenRequestHandler;
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
|
||||
import cn.dev33.satoken.oauth2.logic.SaOAuth2Util;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
public class AuthServerController {
|
||||
|
||||
@Autowired
|
||||
private UserInfoRepository userInfoRepository;
|
||||
|
||||
// 处理所有OAuth相关请求
|
||||
@RequestMapping("/oauth2/*")
|
||||
public Object request() {
|
||||
return TokenRequestHandler.serverRequest();
|
||||
}
|
||||
|
||||
// Sa-OAuth2 定制化配置
|
||||
@Autowired
|
||||
public void setSaOAuth2Config(SaOAuth2Config cfg) {
|
||||
cfg.
|
||||
// 未登录的视图
|
||||
setNotLoginView(() -> new ModelAndView("login.html")).
|
||||
// 登录处理函数
|
||||
setDoLoginHandle((name, pwd) -> {
|
||||
try {
|
||||
UserInfo userInfo = userInfoRepository.findByUid(name);
|
||||
if (userInfo != null) {
|
||||
String secret = userInfo.getSecret();
|
||||
String encodePwd = CodecUtil.aesEncrypt(pwd, Constants.ACCOUNT_SECRET);
|
||||
if (encodePwd.equals(secret)) {
|
||||
StpUtil.login(userInfo.getId(), "PC");
|
||||
return SaResult.ok();
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
return SaResult.error("账号名或密码错误");
|
||||
}
|
||||
return SaResult.error("账号名或密码错误");
|
||||
}).
|
||||
// 授权确认视图
|
||||
setConfirmView((clientId, scope) -> {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("clientId", clientId);
|
||||
map.put("scope", scope);
|
||||
return new ModelAndView("confirm.html", map);
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
// ---------- 开放相关资源接口: Client端根据 Access-Token ,置换相关资源 ------------
|
||||
|
||||
// 获取Userinfo信息:昵称、头像、性别等等
|
||||
@RequestMapping("/oauth2/userinfo")
|
||||
public SaResult userinfo() {
|
||||
// 获取 Access-Token 对应的账号id
|
||||
String accessToken = SaHolder.getRequest().getParamNotNull("access_token");
|
||||
Object loginId = SaOAuth2Util.getLoginIdByAccessToken(accessToken);
|
||||
System.out.println("-------- 此Access-Token对应的账号id: " + loginId);
|
||||
|
||||
// 校验 Access-Token 是否具有权限: userinfo
|
||||
SaOAuth2Util.checkScope(accessToken, "userinfo");
|
||||
|
||||
// 模拟账号信息 (真实环境需要查询数据库获取信息)
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("nickname", "shengzhang_");
|
||||
map.put("avatar", "http://xxx.com/1.jpg");
|
||||
map.put("age", "18");
|
||||
map.put("sex", "男");
|
||||
map.put("address", "山东省 青岛市 城阳区");
|
||||
return SaResult.data(map);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package cc.iotkit.oauth.service;
|
||||
|
||||
import cc.iotkit.common.Constants;
|
||||
import cc.iotkit.common.utils.CodecUtil;
|
||||
import cc.iotkit.dao.OauthClientCache;
|
||||
import cc.iotkit.model.OauthClient;
|
||||
import cn.dev33.satoken.oauth2.logic.SaOAuth2Template;
|
||||
import cn.dev33.satoken.oauth2.model.SaClientModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class SaOAuth2TemplateImpl extends SaOAuth2Template {
|
||||
|
||||
@Autowired
|
||||
private OauthClientCache oauthClientCache;
|
||||
|
||||
// 根据 id 获取 Client 信息
|
||||
@Override
|
||||
public SaClientModel getClientModel(String clientId) {
|
||||
OauthClient client = oauthClientCache.getClient(clientId);
|
||||
if (client == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return new SaClientModel()
|
||||
.setClientId(client.getClientId())
|
||||
.setClientSecret(client.getClientSecret())
|
||||
.setAllowUrl(client.getAllowUrl())
|
||||
.setContractScope("userinfo")
|
||||
.setIsAutoMode(true);
|
||||
}
|
||||
|
||||
// 根据ClientId 和 LoginId 获取openid
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public String getOpenid(String clientId, Object loginId) {
|
||||
// 此为模拟数据,真实环境需要从数据库查询
|
||||
return CodecUtil.aesEncrypt(clientId + ":" + loginId, Constants.ACCOUNT_SECRET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String randomAccessToken(String clientId, Object loginId, String scope) {
|
||||
return StpUtil.createLoginSession(loginId);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package cc.iotkit.oauth.service;
|
||||
|
||||
import cc.iotkit.dao.UserInfoCache;
|
||||
import cc.iotkit.model.UserInfo;
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class StpInterfaceImpl implements StpInterface {
|
||||
|
||||
@Autowired
|
||||
private UserInfoCache userInfoCache;
|
||||
|
||||
/**
|
||||
* 返回一个账号所拥有的权限码集合
|
||||
*/
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
|
||||
List<String> list = new ArrayList<String>();
|
||||
list.add("write");
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)
|
||||
*/
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginType) {
|
||||
UserInfo userInfo = userInfoCache.getUserInfo(loginId.toString());
|
||||
return userInfo.getRoles();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package cc.iotkit.oauth.service;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.context.model.SaResponse;
|
||||
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
|
||||
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
|
||||
import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception;
|
||||
import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts;
|
||||
import cn.dev33.satoken.oauth2.logic.SaOAuth2Handle;
|
||||
import cn.dev33.satoken.oauth2.model.SaClientModel;
|
||||
|
||||
public class TokenRequestHandler {
|
||||
|
||||
public static Object serverRequest() {
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
|
||||
SaClientModel cm;
|
||||
if (req.isPath(SaOAuth2Consts.Api.authorize) && req.isParam(SaOAuth2Consts.Param.response_type, SaOAuth2Consts.ResponseType.code)) {
|
||||
cm = SaOAuth2Handle.currClientModel();
|
||||
if (!cfg.getIsCode() || !cm.isCode && !cm.isAutoMode) {
|
||||
throw new SaOAuth2Exception("暂未开放的授权模式");
|
||||
} else {
|
||||
return SaOAuth2Handle.authorize(req, res, cfg);
|
||||
}
|
||||
} else if (req.isPath(SaOAuth2Consts.Api.token) && req.isParam(SaOAuth2Consts.Param.grant_type, SaOAuth2Consts.GrantType.authorization_code)) {
|
||||
return SaOAuth2Handle.token(req, res, cfg);
|
||||
} else if (req.isPath(SaOAuth2Consts.Api.token) && req.isParam(SaOAuth2Consts.Param.grant_type, SaOAuth2Consts.GrantType.refresh_token)) {
|
||||
return SaOAuth2Handle.refreshToken(req);
|
||||
} else if (req.isPath(SaOAuth2Consts.Api.revoke)) {
|
||||
return SaOAuth2Handle.revokeToken(req);
|
||||
} else if (req.isPath(SaOAuth2Consts.Api.doLogin)) {
|
||||
return SaOAuth2Handle.doLogin(req, res, cfg);
|
||||
} else if (req.isPath(SaOAuth2Consts.Api.doConfirm)) {
|
||||
return SaOAuth2Handle.doConfirm(req);
|
||||
} else if (req.isPath(SaOAuth2Consts.Api.authorize) && req.isParam(SaOAuth2Consts.Param.response_type, SaOAuth2Consts.ResponseType.token)) {
|
||||
cm = SaOAuth2Handle.currClientModel();
|
||||
if (!cfg.getIsImplicit() || !cm.isImplicit && !cm.isAutoMode) {
|
||||
throw new SaOAuth2Exception("暂未开放的授权模式");
|
||||
} else {
|
||||
return SaOAuth2Handle.authorize(req, res, cfg);
|
||||
}
|
||||
} else if (req.isPath(SaOAuth2Consts.Api.token) && req.isParam(SaOAuth2Consts.Param.grant_type, SaOAuth2Consts.GrantType.password)) {
|
||||
cm = SaOAuth2Handle.currClientModel();
|
||||
if (!cfg.getIsPassword() || !cm.isPassword && !cm.isAutoMode) {
|
||||
throw new SaOAuth2Exception("暂未开放的授权模式");
|
||||
} else {
|
||||
return SaOAuth2Handle.password(req, res, cfg);
|
||||
}
|
||||
} else if (req.isPath(SaOAuth2Consts.Api.token) && req.isParam(SaOAuth2Consts.Param.grant_type, SaOAuth2Consts.GrantType.client_credentials)) {
|
||||
cm = SaOAuth2Handle.currClientModel();
|
||||
if (!cfg.getIsClient() || !cm.isClient && !cm.isAutoMode) {
|
||||
throw new SaOAuth2Exception("暂未开放的授权模式");
|
||||
} else {
|
||||
return SaOAuth2Handle.clientToken(req, res, cfg);
|
||||
}
|
||||
} else {
|
||||
return "{\"msg\": \"not handle\"}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,737 @@
|
|||
package cc.iotkit.utils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Map< String, Object> 是最常用的一种Map类型,但是它写着麻烦
|
||||
* <p>所以特封装此类,继承Map,进行一些扩展,可以让Map更灵活使用
|
||||
* <p>最新:2020-12-10 新增部分构造方法
|
||||
* @author kong
|
||||
*/
|
||||
public class SoMap extends LinkedHashMap<String, Object> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public SoMap() {
|
||||
}
|
||||
|
||||
/** 以下元素会在isNull函数中被判定为Null, */
|
||||
public static final Object[] NULL_ELEMENT_ARRAY = {null, ""};
|
||||
public static final List<Object> NULL_ELEMENT_LIST;
|
||||
|
||||
static {
|
||||
NULL_ELEMENT_LIST = Arrays.asList(NULL_ELEMENT_ARRAY);
|
||||
}
|
||||
|
||||
// ============================= 读值 =============================
|
||||
|
||||
/** 获取一个值 */
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
if("this".equals(key)) {
|
||||
return this;
|
||||
}
|
||||
return super.get(key);
|
||||
}
|
||||
|
||||
/** 如果为空,则返回默认值 */
|
||||
public Object get(Object key, Object defaultValue) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/** 转为String并返回 */
|
||||
public String getString(String key) {
|
||||
Object value = get(key);
|
||||
if(value == null) {
|
||||
return null;
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
/** 如果为空,则返回默认值 */
|
||||
public String getString(String key, String defaultValue) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
/** 转为int并返回 */
|
||||
public int getInt(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return 0;
|
||||
}
|
||||
return Integer.valueOf(String.valueOf(value));
|
||||
}
|
||||
/** 转为int并返回,同时指定默认值 */
|
||||
public int getInt(String key, int defaultValue) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Integer.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为long并返回 */
|
||||
public long getLong(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return 0;
|
||||
}
|
||||
return Long.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为double并返回 */
|
||||
public double getDouble(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return 0.0;
|
||||
}
|
||||
return Double.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为boolean并返回 */
|
||||
public boolean getBoolean(String key) {
|
||||
Object value = get(key);
|
||||
if(valueIsNull(value)) {
|
||||
return false;
|
||||
}
|
||||
return Boolean.valueOf(String.valueOf(value));
|
||||
}
|
||||
|
||||
/** 转为Date并返回,根据自定义格式 */
|
||||
public Date getDateByFormat(String key, String format) {
|
||||
try {
|
||||
return new SimpleDateFormat(format).parse(getString(key));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** 转为Date并返回,根据格式: yyyy-MM-dd */
|
||||
public Date getDate(String key) {
|
||||
return getDateByFormat(key, "yyyy-MM-dd");
|
||||
}
|
||||
|
||||
/** 转为Date并返回,根据格式: yyyy-MM-dd HH:mm:ss */
|
||||
public Date getDateTime(String key) {
|
||||
return getDateByFormat(key, "yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
||||
/** 转为Map并返回 */
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public SoMap getMap(String key) {
|
||||
Object value = get(key);
|
||||
if(value == null) {
|
||||
return SoMap.getSoMap();
|
||||
}
|
||||
if(value instanceof Map) {
|
||||
return SoMap.getSoMap((Map)value);
|
||||
}
|
||||
if(value instanceof String) {
|
||||
return SoMap.getSoMap().setJsonString((String)value);
|
||||
}
|
||||
throw new RuntimeException("值无法转化为SoMap: " + value);
|
||||
}
|
||||
|
||||
/** 获取集合(必须原先就是个集合,否则会创建个新集合并返回) */
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Object> getList(String key) {
|
||||
Object value = get(key);
|
||||
List<Object> list = null;
|
||||
if(value == null || value.equals("")) {
|
||||
list = new ArrayList<Object>();
|
||||
}
|
||||
else if(value instanceof List) {
|
||||
list = (List<Object>)value;
|
||||
} else {
|
||||
list = new ArrayList<Object>();
|
||||
list.add(value);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/** 获取集合 (指定泛型类型) */
|
||||
public <T> List<T> getList(String key, Class<T> cs) {
|
||||
List<Object> list = getList(key);
|
||||
List<T> list2 = new ArrayList<T>();
|
||||
for (Object obj : list) {
|
||||
T objC = getValueByClass(obj, cs);
|
||||
list2.add(objC);
|
||||
}
|
||||
return list2;
|
||||
}
|
||||
|
||||
/** 获取集合(逗号分隔式),(指定类型) */
|
||||
public <T> List<T> getListByComma(String key, Class<T> cs) {
|
||||
String listStr = getString(key);
|
||||
if(listStr == null || listStr.equals("")) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
// 开始转化
|
||||
String [] arr = listStr.split(",");
|
||||
List<T> list = new ArrayList<T>();
|
||||
for (String str : arr) {
|
||||
if(cs == int.class || cs == Integer.class || cs == long.class || cs == Long.class) {
|
||||
str = str.trim();
|
||||
}
|
||||
T objC = getValueByClass(str, cs);
|
||||
list.add(objC);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/** 根据指定类型从map中取值,返回实体对象 */
|
||||
public <T> T getModel(Class<T> cs) {
|
||||
try {
|
||||
return getModelByObject(cs.newInstance());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** 从map中取值,塞到一个对象中 */
|
||||
public <T> T getModelByObject(T obj) {
|
||||
// 获取类型
|
||||
Class<?> cs = obj.getClass();
|
||||
// 循环复制
|
||||
for (Field field : cs.getDeclaredFields()) {
|
||||
try {
|
||||
// 获取对象
|
||||
Object value = this.get(field.getName());
|
||||
if(value == null) {
|
||||
continue;
|
||||
}
|
||||
field.setAccessible(true);
|
||||
Object valueConvert = getValueByClass(value, field.getType());
|
||||
field.set(obj, valueConvert);
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
throw new RuntimeException("属性取值出错:" + field.getName(), e);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 将指定值转化为指定类型并返回
|
||||
* @param obj
|
||||
* @param cs
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getValueByClass(Object obj, Class<T> cs) {
|
||||
String obj2 = String.valueOf(obj);
|
||||
Object obj3 = null;
|
||||
if (cs.equals(String.class)) {
|
||||
obj3 = obj2;
|
||||
} else if (cs.equals(int.class) || cs.equals(Integer.class)) {
|
||||
obj3 = Integer.valueOf(obj2);
|
||||
} else if (cs.equals(long.class) || cs.equals(Long.class)) {
|
||||
obj3 = Long.valueOf(obj2);
|
||||
} else if (cs.equals(short.class) || cs.equals(Short.class)) {
|
||||
obj3 = Short.valueOf(obj2);
|
||||
} else if (cs.equals(byte.class) || cs.equals(Byte.class)) {
|
||||
obj3 = Byte.valueOf(obj2);
|
||||
} else if (cs.equals(float.class) || cs.equals(Float.class)) {
|
||||
obj3 = Float.valueOf(obj2);
|
||||
} else if (cs.equals(double.class) || cs.equals(Double.class)) {
|
||||
obj3 = Double.valueOf(obj2);
|
||||
} else if (cs.equals(boolean.class) || cs.equals(Boolean.class)) {
|
||||
obj3 = Boolean.valueOf(obj2);
|
||||
} else {
|
||||
obj3 = (T)obj;
|
||||
}
|
||||
return (T)obj3;
|
||||
}
|
||||
|
||||
|
||||
// ============================= 写值 =============================
|
||||
|
||||
/**
|
||||
* 给指定key添加一个默认值(只有在这个key原来无值的情况先才会set进去)
|
||||
*/
|
||||
public void setDefaultValue(String key, Object defaultValue) {
|
||||
if(isNull(key)) {
|
||||
set(key, defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
/** set一个值,连缀风格 */
|
||||
public SoMap set(String key, Object value) {
|
||||
// 防止敏感key
|
||||
if(key.toLowerCase().equals("this")) {
|
||||
return this;
|
||||
}
|
||||
put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 将一个Map塞进SoMap */
|
||||
public SoMap setMap(Map<String, ?> map) {
|
||||
if(map != null) {
|
||||
for (String key : map.keySet()) {
|
||||
this.set(key, map.get(key));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 将一个对象解析塞进SoMap */
|
||||
public SoMap setModel(Object model) {
|
||||
if(model == null) {
|
||||
return this;
|
||||
}
|
||||
Field[] fields = model.getClass().getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
try{
|
||||
field.setAccessible(true);
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if(!isStatic) {
|
||||
this.set(field.getName(), field.get(model));
|
||||
}
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 将json字符串解析后塞进SoMap */
|
||||
public SoMap setJsonString(String jsonString) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = new ObjectMapper().readValue(jsonString, Map.class);
|
||||
return this.setMap(map);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================= 删值 =============================
|
||||
|
||||
/** delete一个值,连缀风格 */
|
||||
public SoMap delete(String key) {
|
||||
remove(key);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 清理所有value为null的字段 */
|
||||
public SoMap clearNull() {
|
||||
Iterator<String> iterator = this.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
String key = iterator.next();
|
||||
if(this.isNull(key)) {
|
||||
iterator.remove();
|
||||
this.remove(key);
|
||||
}
|
||||
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 清理指定key */
|
||||
public SoMap clearIn(String ...keys) {
|
||||
List<String> keys2 = Arrays.asList(keys);
|
||||
Iterator<String> iterator = this.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
String key = iterator.next();
|
||||
if(keys2.contains(key) == true) {
|
||||
iterator.remove();
|
||||
this.remove(key);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 清理掉不在列表中的key */
|
||||
public SoMap clearNotIn(String ...keys) {
|
||||
List<String> keys2 = Arrays.asList(keys);
|
||||
Iterator<String> iterator = this.keySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
String key = iterator.next();
|
||||
if(keys2.contains(key) == false) {
|
||||
iterator.remove();
|
||||
this.remove(key);
|
||||
}
|
||||
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** 清理掉所有key */
|
||||
public SoMap clearAll() {
|
||||
clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ============================= 快速构建 =============================
|
||||
|
||||
/** 构建一个SoMap并返回 */
|
||||
public static SoMap getSoMap() {
|
||||
return new SoMap();
|
||||
}
|
||||
/** 构建一个SoMap并返回 */
|
||||
public static SoMap getSoMap(String key, Object value) {
|
||||
return new SoMap().set(key, value);
|
||||
}
|
||||
/** 构建一个SoMap并返回 */
|
||||
public static SoMap getSoMap(Map<String, ?> map) {
|
||||
return new SoMap().setMap(map);
|
||||
}
|
||||
|
||||
/** 将一个对象集合解析成为SoMap */
|
||||
public static SoMap getSoMapByModel(Object model) {
|
||||
return SoMap.getSoMap().setModel(model);
|
||||
}
|
||||
|
||||
/** 将一个对象集合解析成为SoMap集合 */
|
||||
public static List<SoMap> getSoMapByList(List<?> list) {
|
||||
List<SoMap> listMap = new ArrayList<SoMap>();
|
||||
for (Object model : list) {
|
||||
listMap.add(getSoMapByModel(model));
|
||||
}
|
||||
return listMap;
|
||||
}
|
||||
|
||||
/** 克隆指定key,返回一个新的SoMap */
|
||||
public SoMap cloneKeys(String... keys) {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : keys) {
|
||||
so.set(key, this.get(key));
|
||||
}
|
||||
return so;
|
||||
}
|
||||
/** 克隆所有key,返回一个新的SoMap */
|
||||
public SoMap cloneSoMap() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(key, this.get(key));
|
||||
}
|
||||
return so;
|
||||
}
|
||||
|
||||
/** 将所有key转为大写 */
|
||||
public SoMap toUpperCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(key.toUpperCase(), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key转为小写 */
|
||||
public SoMap toLowerCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(key.toLowerCase(), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key中下划线转为中划线模式 (kebab-case风格) */
|
||||
public SoMap toKebabCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(wordEachKebabCase(key), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key中下划线转为小驼峰模式 */
|
||||
public SoMap toHumpCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(wordEachBigFs(key), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
/** 将所有key中小驼峰转为下划线模式 */
|
||||
public SoMap humpToLineCase() {
|
||||
SoMap so = new SoMap();
|
||||
for (String key : this.keySet()) {
|
||||
so.set(wordHumpToLine(key), this.get(key));
|
||||
}
|
||||
this.clearAll().setMap(so);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ============================= 辅助方法 =============================
|
||||
|
||||
|
||||
/** 指定key是否为null,判定标准为 NULL_ELEMENT_ARRAY 中的元素 */
|
||||
public boolean isNull(String key) {
|
||||
return valueIsNull(get(key));
|
||||
}
|
||||
|
||||
/** 指定key列表中是否包含value为null的元素,只要有一个为null,就会返回true */
|
||||
public boolean isContainNull(String ...keys) {
|
||||
for (String key : keys) {
|
||||
if(this.isNull(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 与isNull()相反 */
|
||||
public boolean isNotNull(String key) {
|
||||
return !isNull(key);
|
||||
}
|
||||
/** 指定key的value是否为null,作用同isNotNull() */
|
||||
public boolean has(String key) {
|
||||
return !isNull(key);
|
||||
}
|
||||
|
||||
/** 指定value在此SoMap的判断标准中是否为null */
|
||||
public boolean valueIsNull(Object value) {
|
||||
return NULL_ELEMENT_LIST.contains(value);
|
||||
}
|
||||
|
||||
/** 验证指定key不为空,为空则抛出异常 */
|
||||
public SoMap checkNull(String ...keys) {
|
||||
for (String key : keys) {
|
||||
if(this.isNull(key)) {
|
||||
throw new RuntimeException("参数" + key + "不能为空");
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
static Pattern patternNumber = Pattern.compile("[0-9]*");
|
||||
/** 指定key是否为数字 */
|
||||
public boolean isNumber(String key) {
|
||||
String value = getString(key);
|
||||
if(value == null) {
|
||||
return false;
|
||||
}
|
||||
return patternNumber.matcher(value).matches();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 转为JSON字符串
|
||||
*/
|
||||
public String toJsonString() {
|
||||
try {
|
||||
// SoMap so = SoMap.getSoMap(this);
|
||||
return new ObjectMapper().writeValueAsString(this);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 转为JSON字符串, 带格式的
|
||||
// */
|
||||
// public String toJsonFormatString() {
|
||||
// try {
|
||||
// return JSON.toJSONString(this, true);
|
||||
// } catch (Exception e) {
|
||||
// throw new RuntimeException(e);
|
||||
// }
|
||||
// }
|
||||
|
||||
// ============================= web辅助 =============================
|
||||
|
||||
|
||||
/**
|
||||
* 返回当前request请求的的所有参数
|
||||
* @return
|
||||
*/
|
||||
public static SoMap getRequestSoMap() {
|
||||
// 大善人SpringMVC提供的封装
|
||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if(servletRequestAttributes == null) {
|
||||
throw new RuntimeException("当前线程非JavaWeb环境");
|
||||
}
|
||||
// 当前request
|
||||
HttpServletRequest request = servletRequestAttributes.getRequest();
|
||||
if (request.getAttribute("currentSoMap") == null || request.getAttribute("currentSoMap") instanceof SoMap == false ) {
|
||||
initRequestSoMap(request);
|
||||
}
|
||||
return (SoMap)request.getAttribute("currentSoMap");
|
||||
}
|
||||
|
||||
/** 初始化当前request的 SoMap */
|
||||
private static void initRequestSoMap(HttpServletRequest request) {
|
||||
SoMap soMap = new SoMap();
|
||||
Map<String, String[]> parameterMap = request.getParameterMap(); // 获取所有参数
|
||||
for (String key : parameterMap.keySet()) {
|
||||
try {
|
||||
String[] values = parameterMap.get(key); // 获得values
|
||||
if(values.length == 1) {
|
||||
soMap.set(key, values[0]);
|
||||
} else {
|
||||
List<String> list = new ArrayList<String>();
|
||||
for (String v : values) {
|
||||
list.add(v);
|
||||
}
|
||||
soMap.set(key, list);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
request.setAttribute("currentSoMap", soMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证返回当前线程是否为JavaWeb环境
|
||||
* @return
|
||||
*/
|
||||
public static boolean isJavaWeb() {
|
||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// 大善人SpringMVC提供的封装
|
||||
if(servletRequestAttributes == null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============================= 常见key (以下key经常用,所以封装以下,方便写代码) =============================
|
||||
|
||||
/** get 当前页 */
|
||||
public int getKeyPageNo() {
|
||||
int pageNo = getInt("pageNo", 1);
|
||||
if(pageNo <= 0) {
|
||||
pageNo = 1;
|
||||
}
|
||||
return pageNo;
|
||||
}
|
||||
/** get 页大小 */
|
||||
public int getKeyPageSize() {
|
||||
int pageSize = getInt("pageSize", 10);
|
||||
if(pageSize <= 0 || pageSize > 1000) {
|
||||
pageSize = 10;
|
||||
}
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
/** get 排序方式 */
|
||||
public int getKeySortType() {
|
||||
return getInt("sortType");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ============================= 工具方法 =============================
|
||||
|
||||
|
||||
/**
|
||||
* 将一个一维集合转换为树形集合
|
||||
* @param list 集合
|
||||
* @param idKey id标识key
|
||||
* @param parentIdKey 父id标识key
|
||||
* @param childListKey 子节点标识key
|
||||
* @return 转换后的tree集合
|
||||
*/
|
||||
public static List<SoMap> listToTree(List<SoMap> list, String idKey, String parentIdKey, String childListKey) {
|
||||
// 声明新的集合,存储tree形数据
|
||||
List<SoMap> newTreeList = new ArrayList<SoMap>();
|
||||
// 声明hash-Map,方便查找数据
|
||||
SoMap hash = new SoMap();
|
||||
// 将数组转为Object的形式,key为数组中的id
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
SoMap json = (SoMap) list.get(i);
|
||||
hash.put(json.getString(idKey), json);
|
||||
}
|
||||
// 遍历结果集
|
||||
for (int j = 0; j < list.size(); j++) {
|
||||
// 单条记录
|
||||
SoMap aVal = (SoMap) list.get(j);
|
||||
// 在hash中取出key为单条记录中pid的值
|
||||
SoMap hashVp = (SoMap) hash.get(aVal.get(parentIdKey, "").toString());
|
||||
// 如果记录的pid存在,则说明它有父节点,将她添加到孩子节点的集合中
|
||||
if (hashVp != null) {
|
||||
// 检查是否有child属性,有则添加,没有则新建
|
||||
if (hashVp.get(childListKey) != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<SoMap> ch = (List<SoMap>) hashVp.get(childListKey);
|
||||
ch.add(aVal);
|
||||
hashVp.put(childListKey, ch);
|
||||
} else {
|
||||
List<SoMap> ch = new ArrayList<SoMap>();
|
||||
ch.add(aVal);
|
||||
hashVp.put(childListKey, ch);
|
||||
}
|
||||
} else {
|
||||
newTreeList.add(aVal);
|
||||
}
|
||||
}
|
||||
return newTreeList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 指定字符串的字符串下划线转大写模式 */
|
||||
private static String wordEachBig(String str){
|
||||
String newStr = "";
|
||||
for (String s : str.split("_")) {
|
||||
newStr += wordFirstBig(s);
|
||||
}
|
||||
return newStr;
|
||||
}
|
||||
/** 返回下划线转小驼峰形式 */
|
||||
private static String wordEachBigFs(String str){
|
||||
return wordFirstSmall(wordEachBig(str));
|
||||
}
|
||||
|
||||
/** 将指定单词首字母大写 */
|
||||
private static String wordFirstBig(String str) {
|
||||
return str.substring(0, 1).toUpperCase() + str.substring(1, str.length());
|
||||
}
|
||||
|
||||
/** 将指定单词首字母小写 */
|
||||
private static String wordFirstSmall(String str) {
|
||||
return str.substring(0, 1).toLowerCase() + str.substring(1, str.length());
|
||||
}
|
||||
|
||||
/** 下划线转中划线 */
|
||||
private static String wordEachKebabCase(String str) {
|
||||
return str.replaceAll("_", "-");
|
||||
}
|
||||
|
||||
/** 驼峰转下划线 */
|
||||
private static String wordHumpToLine(String str) {
|
||||
return str.replaceAll("[A-Z]", "_$0").toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>奇特物联认证授权</title>
|
||||
<style type="text/css">
|
||||
body{
|
||||
min-height: 500px;
|
||||
background: linear-gradient( 270deg, rgb(46 124 255) 0%, rgb(52 3 65) 100% );
|
||||
}
|
||||
.bgimg{
|
||||
position:absolute;
|
||||
background-image:url(http://iotkit-img.oss-cn-shenzhen.aliyuncs.com/product/KdJYpTp5ywNhmrmC/cover.png?Expires=1968261336&OSSAccessKeyId=LTAI5t8UFEH5eGrBUS5zSiof&Signature=df%2F6JEcxBlXitSNIENPMYJlRE8Y%3D);
|
||||
background-size: 100% 100%;
|
||||
width:60%;
|
||||
height:60%;
|
||||
margin-top:10%;
|
||||
}
|
||||
*{margin: 0px; padding: 0px;}
|
||||
.login-box{
|
||||
position: absolute;
|
||||
right: 100px;
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
margin: 20vh auto;
|
||||
padding: 30px 50px;
|
||||
background-color: #fff;
|
||||
overflow: inherit;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.login-box .title{color:#333;font-size:22px;text-align:center;margin:20px 20px}
|
||||
.login-box button{padding: 5px 15px; cursor: pointer; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bgimg">
|
||||
</div>
|
||||
<div class="login-box">
|
||||
<div class="title">奇特物联授权确认</div> <br>
|
||||
<div>
|
||||
正在确认授权...
|
||||
</div>
|
||||
<div id="divFailed"></div>
|
||||
</div>
|
||||
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
|
||||
<script>window.jQuery || alert('当前页面CDN服务商已宕机,请将所有js包更换为本地依赖')</script>
|
||||
<script type="text/javascript">
|
||||
// 同意授权
|
||||
function yes() {
|
||||
$.ajax({
|
||||
url: '/oauth2/doConfirm',
|
||||
data: {
|
||||
client_id: getParam('client_id'),
|
||||
scope: getParam('scope')
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
if(res.code == 200) {
|
||||
location.reload(true);
|
||||
} else {
|
||||
// 重定向至授权失败URL
|
||||
$("#divFailed").text('授权失败!');
|
||||
}
|
||||
},
|
||||
error: function(e) {
|
||||
console.log('error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 拒绝授权
|
||||
function no() {
|
||||
var url = joinParam(getParam('redirect_uri'), "handle=refuse&msg=用户拒绝了授权");
|
||||
location.href = url;
|
||||
}
|
||||
|
||||
// 从url中查询到指定名称的参数值
|
||||
function getParam(name, defaultValue){
|
||||
var query = window.location.search.substring(1);
|
||||
var vars = query.split("&");
|
||||
for (var i=0;i<vars.length;i++) {
|
||||
var pair = vars[i].split("=");
|
||||
if(pair[0] == name){return pair[1];}
|
||||
}
|
||||
return(defaultValue == undefined ? null : defaultValue);
|
||||
}
|
||||
|
||||
// 在url上拼接上kv参数并返回
|
||||
function joinParam(url, parameStr) {
|
||||
if(parameStr == null || parameStr.length == 0) {
|
||||
return url;
|
||||
}
|
||||
var index = url.indexOf('?');
|
||||
// ? 不存在
|
||||
if(index == -1) {
|
||||
return url + '?' + parameStr;
|
||||
}
|
||||
// ? 是最后一位
|
||||
if(index == url.length - 1) {
|
||||
return url + parameStr;
|
||||
}
|
||||
// ? 是其中一位
|
||||
if(index > -1 && index < url.length - 1) {
|
||||
// 如果最后一位是 不是&, 且 parameStr 第一位不是 &, 就增送一个 &
|
||||
if(url.lastIndexOf('&') != url.length - 1 && parameStrindexOf('&') != 0) {
|
||||
return url + '&' + parameStr;
|
||||
} else {
|
||||
return url + parameStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yes();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,123 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>奇特物联登录授权</title>
|
||||
<style type="text/css">
|
||||
body{
|
||||
min-height: 500px;
|
||||
background: linear-gradient( 270deg, rgb(46 124 255) 0%, rgb(52 3 65) 100% );
|
||||
}
|
||||
.bgimg{
|
||||
position:absolute;
|
||||
background-image:url(http://iotkit-img.oss-cn-shenzhen.aliyuncs.com/product/KdJYpTp5ywNhmrmC/cover.png?Expires=1968261336&OSSAccessKeyId=LTAI5t8UFEH5eGrBUS5zSiof&Signature=df%2F6JEcxBlXitSNIENPMYJlRE8Y%3D);
|
||||
background-size: 100% 100%;
|
||||
width:60%;
|
||||
height:60%;
|
||||
margin-top:10%;
|
||||
}
|
||||
*{margin: 0px; padding: 0px;}
|
||||
.login-box{
|
||||
position: absolute;
|
||||
right: 100px;
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
margin: 20vh auto;
|
||||
padding: 30px 50px;
|
||||
background-color: #fff;
|
||||
overflow: inherit;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.login-box .title{color:#333;font-size:22px;text-align:center;margin:20px 20px}
|
||||
.login-box .form{list-style:none; padding:20px 20px;}
|
||||
.login-box li{
|
||||
padding:10px 10px;
|
||||
line-height:30px;
|
||||
text-align:center;
|
||||
}
|
||||
.login-box input{
|
||||
width:100%;
|
||||
line-height: 30px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #999;
|
||||
padding:5px 10px;
|
||||
}
|
||||
.login-box button{
|
||||
padding: 5px 15px;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
line-height:30px;
|
||||
border: 1px solid #5e77ff;
|
||||
background-color: #5e77ff;
|
||||
color: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.login-box button:hover{
|
||||
background-color: #2746f1;
|
||||
}
|
||||
.login-box .tip{
|
||||
margin-top:20px;
|
||||
color:#666;
|
||||
}
|
||||
.tip.error{
|
||||
color:red;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bgimg">
|
||||
</div>
|
||||
<div class="login-box">
|
||||
<div class="title">奇特物联登录授权</div>
|
||||
<ul class="form">
|
||||
<li>
|
||||
<input name="name" placeholder="请输入账号"/>
|
||||
</li>
|
||||
<li>
|
||||
<input name="pwd" type="password" placeholder="请输入密码" /> <br>
|
||||
</li>
|
||||
<li>
|
||||
<button onclick="doLogin()">登录</button>
|
||||
</li>
|
||||
<li class="tip" id="liTip"></li>
|
||||
<li style="color:#aaa;">
|
||||
Copyright © 2021 - 2022 奇特物联
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
|
||||
<script>window.jQuery || alert('当前页面CDN服务商已宕机,请将所有js包更换为本地依赖')</script>
|
||||
<script type="text/javascript">
|
||||
// 登录方法
|
||||
function doLogin() {
|
||||
var name=$.trim($('[name=name]').val());
|
||||
var pwd=$.trim($('[name=pwd]').val());
|
||||
if(!name || !pwd){
|
||||
$("#liTip").addClass("error").text("账号或密码不能为空!");
|
||||
}
|
||||
$.ajax({
|
||||
url: '/oauth2/doLogin',
|
||||
data: {
|
||||
name: name,
|
||||
pwd: pwd
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
if(res.code == 200) {
|
||||
$("#liTip").removeClass("error").text("登录成功!");
|
||||
setTimeout(function() {
|
||||
location.reload(true);
|
||||
}, 800);
|
||||
} else {
|
||||
$("#liTip").addClass("error").text(res.msg);
|
||||
}
|
||||
},
|
||||
error: function(e) {
|
||||
console.log('error');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
47
pom.xml
47
pom.xml
|
@ -11,6 +11,7 @@
|
|||
<module>dao</module>
|
||||
<module>protocol-gateway</module>
|
||||
<module>standalone-package</module>
|
||||
<module>oauth2-server</module>
|
||||
</modules>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -21,13 +22,13 @@
|
|||
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<artifactId>iotkit-parent</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
<name>iotkit-parent</name>
|
||||
<description>iotkit parent</description>
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
<keycloak-spring.version>17.0.0</keycloak-spring.version>
|
||||
<vertx.version>4.2.2</vertx.version>
|
||||
<sa-token.version>1.30.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
@ -96,21 +97,21 @@
|
|||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-spring-boot-starter</artifactId>
|
||||
<version>${keycloak-spring.version}</version>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-oauth2</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-spring-security-adapter</artifactId>
|
||||
<version>${keycloak-spring.version}</version>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-admin-client</artifactId>
|
||||
<version>${keycloak-spring.version}</version>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-dao-redis-jackson</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@ -179,6 +180,24 @@
|
|||
<version>3.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>it.ozimov</groupId>
|
||||
<artifactId>embedded-redis</artifactId>
|
||||
<version>0.7.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<version>2.17.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.ejlchina</groupId>
|
||||
<artifactId>okhttps</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.vertx</groupId>
|
||||
<artifactId>vertx-core</artifactId>
|
||||
|
@ -257,6 +276,12 @@
|
|||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<artifactId>oauth2-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>protocol-gateway</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>protocol-gateway</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>protocol-gateway</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<parent>
|
||||
<artifactId>protocol-gateway</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>emqx-component</artifactId>
|
||||
|
@ -61,19 +61,19 @@
|
|||
<dependency>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<artifactId>model</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<artifactId>component</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>protocol-gateway</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<parent>
|
||||
<artifactId>protocol-gateway</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>http-biz-component</artifactId>
|
||||
|
@ -58,7 +58,7 @@
|
|||
<dependency>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<artifactId>component</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>protocol-gateway</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>protocol-gateway</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<description>mqtt客户端模拟器</description>
|
||||
|
|
|
@ -11,8 +11,8 @@ public class Application {
|
|||
public static void main(String[] args) throws IOException {
|
||||
|
||||
if (args.length == 0) {
|
||||
// Mqtt.broker = "tcp://127.0.0.1:1883";
|
||||
Mqtt.broker = "tcp://120.76.96.206:1883";
|
||||
Mqtt.broker = "tcp://127.0.0.1:1883";
|
||||
// Mqtt.broker = "tcp://120.76.96.206:1883";
|
||||
} else {
|
||||
Mqtt.broker = args[0];
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<parent>
|
||||
<artifactId>protocol-gateway</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>mqtt-component</artifactId>
|
||||
|
@ -80,19 +80,19 @@
|
|||
<dependency>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<artifactId>component</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<artifactId>dao</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>protocol-gateway</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>iotkit-parent</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>iotkit-parent</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>iotkit-parent</artifactId>
|
||||
<groupId>cc.iotkit</groupId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
Loading…
Reference in New Issue