diff --git a/mobile-app/src/main/java/com/ruoyi/chat/ab/entity/ChatFileData.java b/mobile-app/src/main/java/com/ruoyi/chat/ab/entity/ChatFileData.java index 185c5ab7125a15338cec5286287388059ff0750d..88a2bada9a9ecee87b47df29ce849617c80b5537 100644 --- a/mobile-app/src/main/java/com/ruoyi/chat/ab/entity/ChatFileData.java +++ b/mobile-app/src/main/java/com/ruoyi/chat/ab/entity/ChatFileData.java @@ -8,7 +8,7 @@ import java.io.Serializable; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; -import com.sun.istack.NotNull; +import com.sun.istack.internal.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; diff --git a/mobile-app/src/main/java/com/ruoyi/data/controller/DataAppController.java b/mobile-app/src/main/java/com/ruoyi/data/controller/DataAppController.java new file mode 100644 index 0000000000000000000000000000000000000000..74515e350fecae089df68d17f5c5fbd99cf47b68 --- /dev/null +++ b/mobile-app/src/main/java/com/ruoyi/data/controller/DataAppController.java @@ -0,0 +1,157 @@ +package com.ruoyi.data.controller; + + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.uuid.UUID; +import com.ruoyi.data.filter.AccessTokenService; +import com.ruoyi.common.core.domain.entity.data.AccessBody; +import com.ruoyi.data.entity.DataApp; +import com.ruoyi.data.entity.DataKey; +import com.ruoyi.data.service.IDataAppService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/data/app") +public class DataAppController extends BaseController { + + @Autowired + private IDataAppService dataAppService; + + @Autowired + private AccessTokenService accessTokenService; + + + /** + * 获取应用列表 + */ + @GetMapping("/list") + public TableDataInfo list(DataApp app) + { + + DataKey dataKey = new DataKey(); + dataKey.setUserId(getUserId()); + app.setDataKey(dataKey); + + startPage(); + List list = dataAppService.selectAppList(app); + return getDataTable(list); + } + + /** + * + * @param appId + * @param userId + * @param appKey + * @return + */ + @GetMapping("/getAccessToken") + public AjaxResult getAccessToken(@RequestParam(required = false)String appId,@RequestParam(required = false)Long userId,@RequestParam(required = false)String appKey){ + if(userId == null){ + userId = getUserId(); + } + AjaxResult ajax = new AjaxResult(); + if(appKey == null || appId == null){ + return AjaxResult.error(301,"请先申请key值"); + }else { + //判断key值是否正确 + if(dataAppService.checkByAppKeyAndAppId(new DataKey(Long.valueOf(appId),userId,appKey)) > 0){ + String accessToken = accessTokenService.createAccessToken(new AccessBody(Long.valueOf(appId),userId,appKey)); + ajax.put("accessToken",accessToken); + }else { + return AjaxResult.error(302,"请输入正确的key值"); + } + + } + return ajax; + } + + + /** + * 用户申请key + * @param appId + */ + @GetMapping("/getKey") + public AjaxResult getKey(String appId){ + Long userId = getUserId(); + String appKey = UUID.randomUUID().toString(); + DataKey dataKey = new DataKey(Long.valueOf(appId),userId,appKey); + + return toAjax(dataAppService.insertKey(dataKey)); + +// if(row > 0){ +// return getAccessToken(appId,userId,appKey); +// }else { +// return toAjax(row); +// } + + + } + + /** + * 根据用户编号获取详细信息 + */ + @GetMapping(value = {"/{id}" }) + public AjaxResult getInfo(@PathVariable(value = "id", required = false) Long id) + { + + DataApp dataApp = dataAppService.selectById(id); + AjaxResult ajax = AjaxResult.success(); + ajax.put("dataApp",dataApp); + return ajax; + } + + /** + * 新增应用 + */ + @Log(title = "应用管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody DataApp dataApp) + { + if (dataAppService.checkAppNameUnique(dataApp.getAppName()) != null) + { + return AjaxResult.error("新增应用" + dataApp.getAppName() + "'失败,应用名称已存在'"); + } + else if (dataAppService.checkUrlUnique(dataApp.getUrl()) != null) + { + return AjaxResult.error("新增应用'" + dataApp.getUrl() + "'失败,地址已存在"); + } + dataApp.setCreateBy(getUsername()); + return toAjax(dataAppService.insert(dataApp)); + } + + /** + * 修改应用 + */ + @Log(title = "应用管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody DataApp dataApp) + { + + if (dataAppService.checkUrlUnique(dataApp.getUrl()) != null) + { + return AjaxResult.error("修改应用'" + dataApp.getUrl() + "'失败,地址已存在"); + } + dataApp.setUpdateBy(getUsername()); + return toAjax(dataAppService.updataApp(dataApp)); + } + + /** + * 删除应用 + */ + @Log(title = "应用管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + + return toAjax(dataAppService.deleteAppById(ids)); + } + +} diff --git a/mobile-app/src/main/java/com/ruoyi/data/entity/DataApp.java b/mobile-app/src/main/java/com/ruoyi/data/entity/DataApp.java new file mode 100644 index 0000000000000000000000000000000000000000..753957f74cb58fd7ed4cd722bb174e23795b7b55 --- /dev/null +++ b/mobile-app/src/main/java/com/ruoyi/data/entity/DataApp.java @@ -0,0 +1,87 @@ +package com.ruoyi.data.entity; + + +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 数据中心 应用管理 + */ +public class DataApp extends BaseEntity { + + + private static final long serialVersionUID = 1L; + + //应用id + private Long id; + //应用名称 + private String appName; + //应用简介 + private String text; + //分类 + private String classify; + //访问接口 + private String url; + + //用户key值 + private DataKey dataKey; + + //状态吗 (是否被申请) + private int status; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + + public String getClassify() { + return classify; + } + + public void setClassify(String classify) { + this.classify = classify; + } + + public DataKey getDataKey() { + return dataKey; + } + + public void setDataKey(DataKey dataKey) { + this.dataKey = dataKey; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} diff --git a/mobile-app/src/main/java/com/ruoyi/data/entity/DataKey.java b/mobile-app/src/main/java/com/ruoyi/data/entity/DataKey.java new file mode 100644 index 0000000000000000000000000000000000000000..c3e4824267d71bcd312942aa53818b9dc7a22eb7 --- /dev/null +++ b/mobile-app/src/main/java/com/ruoyi/data/entity/DataKey.java @@ -0,0 +1,73 @@ +package com.ruoyi.data.entity; + +import com.ruoyi.common.core.domain.BaseEntity; + + +/** + * 用户 申请 应用的key值 + */ +public class DataKey extends BaseEntity { + + private static final long serialVersionUID = 1L; + + + private Long id; + + private Long appId; + + private Long userId; + + private String appKey; + + private String url; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public DataKey() { + } + + public DataKey(Long appId, Long userId, String appKey) { + this.appId = appId; + this.userId = userId; + this.appKey = appKey; + } + + public String getAppKey() { + return appKey; + } + + public void setAppKey(String appKey) { + this.appKey = appKey; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getAppId() { + return appId; + } + + public void setAppId(Long appId) { + this.appId = appId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + +} diff --git a/mobile-app/src/main/java/com/ruoyi/data/mapper/DataAppMapper.java b/mobile-app/src/main/java/com/ruoyi/data/mapper/DataAppMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..cbd8ed75dc16b4e77fcb1d7076b103d00ee1aed7 --- /dev/null +++ b/mobile-app/src/main/java/com/ruoyi/data/mapper/DataAppMapper.java @@ -0,0 +1,86 @@ +package com.ruoyi.data.mapper; + +import com.ruoyi.data.entity.DataApp; +import com.ruoyi.data.entity.DataKey; + +import java.util.List; + +public interface DataAppMapper { + + /** + * 根据条件分页查询应用列表 + * @param app + * @return + */ + public List selectAppList(DataApp app); + + /** + * 用户申请应用key值 + * @param dataKey + * @return + */ + public int insertKey(DataKey dataKey); + + + /** + * 通过ID进行查询 + * @param id + * @return + */ + public DataApp selectById(Long id); + + + /** + * 校验用户名称是否唯一 + * + * @param appName 应用名称 + * @return 结果 + */ + public DataApp checkAppNameUnique(String appName); + + /** + * 校验手机号码是否唯一 + * + * @param url 地址 + * @return 结果 + */ + public DataApp checkUrlUnique(String url); + + /** + * 新增应用信息 + * @param dataApp + * @return + */ + public int insert(DataApp dataApp); + + + /** + * 修改信息 + * @param dataApp + * @return + */ + public int updataApp(DataApp dataApp); + + /** + * + * @param ids + * @return + */ + public int deleteAppById(Long[] ids); + + + /** + * + * @param userId + * @return + */ + public List selectAppsByUserId(Long userId); + + + /** + * 判断输入的key值是否正确 + * @param dataKey + * @return + */ + public List checkByAppKeyAndAppId(DataKey dataKey); +} diff --git a/mobile-app/src/main/java/com/ruoyi/data/service/IDataAppService.java b/mobile-app/src/main/java/com/ruoyi/data/service/IDataAppService.java new file mode 100644 index 0000000000000000000000000000000000000000..4220cf84d6567ea2154850d83bbb47df87822c36 --- /dev/null +++ b/mobile-app/src/main/java/com/ruoyi/data/service/IDataAppService.java @@ -0,0 +1,92 @@ +package com.ruoyi.data.service; + + +import com.ruoyi.data.entity.DataApp; +import com.ruoyi.data.entity.DataKey; + +import java.util.List; +import java.util.Map; + +/** + * + * 应用管理业务层 + * + */ +public interface IDataAppService { + + /** + * 根据条件分页查询应用列表 + * @param app + * @return + */ + public List selectAppList(DataApp app); + + /** + * 用户申请应用key值 + * @param dataKey + * @return + */ + public int insertKey(DataKey dataKey); + + + /** + * 通过ID进行查询 + * @param id + * @return + */ + public DataApp selectById(Long id); + + /** + * 校验用户名称是否唯一 + * + * @param appName 应用名称 + * @return 结果 + */ + public DataApp checkAppNameUnique(String appName); + + /** + * 校验手机号码是否唯一 + * + * @param url 地址 + * @return 结果 + */ + public DataApp checkUrlUnique(String url); + + /** + * 新增应用信息 + * @param dataApp + * @return + */ + public int insert(DataApp dataApp); + + + /** + * 修改用户信息 + * @param dataApp + * @return + */ + public int updataApp(DataApp dataApp); + + /** + * + * @param ids + * @return + */ + public int deleteAppById(Long[] ids); + + + /** + * 获取用户的 + * @param userId + * @return + */ + public Map> selectAppsByUserId(Long userId); + + /** + * 判断输入的key值是否正确 + * @param dataKey + * @return + */ + public int checkByAppKeyAndAppId(DataKey dataKey); + +} diff --git a/mobile-app/src/main/java/com/ruoyi/data/service/impl/DataAppServiceImpl.java b/mobile-app/src/main/java/com/ruoyi/data/service/impl/DataAppServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..78f4f9f51faefa7b680e7e959065deb08af887a3 --- /dev/null +++ b/mobile-app/src/main/java/com/ruoyi/data/service/impl/DataAppServiceImpl.java @@ -0,0 +1,111 @@ +package com.ruoyi.data.service.impl; + + +import com.ruoyi.data.entity.DataApp; +import com.ruoyi.data.entity.DataKey; +import com.ruoyi.data.mapper.DataAppMapper; +import com.ruoyi.data.service.IDataAppService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class DataAppServiceImpl implements IDataAppService { + + @Autowired + private DataAppMapper dataAppMapper; + + @Override + public List selectAppList(DataApp app) { + return dataAppMapper.selectAppList(app); + } + + /** + * 用户申请应用key值 + * @param dataKey + * @return + */ + @Override + public int insertKey(DataKey dataKey){ + return dataAppMapper.insertKey(dataKey); + } + + + @Override + public DataApp selectById(Long id) { + return dataAppMapper.selectById(id); + } + + /** + * 校验用户名称是否唯一 + * + * @param appName 应用名称 + * @return 结果 + */ + public DataApp checkAppNameUnique(String appName){ + return dataAppMapper.checkAppNameUnique(appName); + } + + /** + * 校验url是否唯一 + * + * @param url 地址 + * @return 结果 + */ + public DataApp checkUrlUnique(String url){ + return dataAppMapper.checkUrlUnique(url); + } + + /** + * 新增应用信息 + * @param dataApp + * @return + */ + public int insert(DataApp dataApp){ + return dataAppMapper.insert(dataApp); + } + + + /** + * 修改用户信息 + * @param dataApp + * @return + */ + public int updataApp(DataApp dataApp){ + return dataAppMapper.updataApp(dataApp); + } + + /** + * + * @param ids + * @return + */ + public int deleteAppById(Long[] ids){ + + return dataAppMapper.deleteAppById(ids); + } + + @Override + public Map> selectAppsByUserId(Long userId) { + + Map> map = new HashMap<>(); + + Listlist = dataAppMapper.selectAppsByUserId(userId); + + for( DataKey dataKey : list){ + Map map1 = new HashMap<>(); + map1.put("key",dataKey.getAppId().toString()); + map1.put("value",dataKey.getAppKey()); + map.put(dataKey.getUrl(),map1); + } + return map; + } + + @Override + public int checkByAppKeyAndAppId(DataKey dataKey) { + return dataAppMapper.checkByAppKeyAndAppId(dataKey).size(); + } +} diff --git a/mobile-app/src/main/resources/mapper/data/DataAppMapper.xml b/mobile-app/src/main/resources/mapper/data/DataAppMapper.xml new file mode 100755 index 0000000000000000000000000000000000000000..0b9586fcab37862ecc0e61dbd7934849ed23fbc7 --- /dev/null +++ b/mobile-app/src/main/resources/mapper/data/DataAppMapper.xml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select id, app_name, text, url,classify, create_by, create_time ,update_by,update_time ,remark + from data_app + + + + + + + + + + + insert into data_key ( + + app_id, + user_id, + app_key, + create_time + + ) values ( + + #{appId} , + #{userId}, + #{appKey} , + sysdate() + + ) + + + + + insert into data_app ( + + app_name, + text, + url, + create_time + )values ( + #{appName} , + #{text} , + #{text} , + sysdate() + ) + + + + + + + + + delete from data_app where id in + + #{id} + + + + + update data_app + + app_name = #{appName}, + text = #{text}, + url = #{url}, + update_time = sysdate() + + where id = #{id} + + + + + + \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 3eaf6c971aa76423de84c10225f48ed003f06463..2a8df2ed7507d5920fad7e01b5604425cdef6611 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -10,6 +10,7 @@ ruoyi: demoEnabled: true # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) profile: D:/ruoyi/uploadPath +# profile: /Users/liyihao/IdeaProjects/app-course-platform/uploadPath # 获取ip地址开关 addressEnabled: false # 验证码类型 math 数组计算 char 字符验证 @@ -38,6 +39,9 @@ logging: # Spring配置 spring: + #bean 不能注册 + main: + allow-bean-definition-overriding: true # 资源信息 messages: # 国际化资源文件路径 @@ -88,6 +92,17 @@ token: # 令牌有效期(默认30分钟) expireTime: 30 + +# accessToken配置 +accessToken: + # 令牌自定义标识 + header: AccessToken + # 令牌密钥 + secret: abcdefghijklmnopqrstuvwxyz + # 令牌有效期(默认30分钟) + expireTime: 30 + + # MyBatis配置 mybatis: # 搜索指定包别名 diff --git a/ruoyi-admin/src/main/resources/logback.xml b/ruoyi-admin/src/main/resources/logback.xml index a360583fa4424fe5b8b88b39eca0a5aa203e7064..8e8977fc632c1ce1ec9512c272bd578fbd72c1f2 100644 --- a/ruoyi-admin/src/main/resources/logback.xml +++ b/ruoyi-admin/src/main/resources/logback.xml @@ -1,6 +1,7 @@ + diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java index d36253cde9acd39b4bac0fad23fb24681e2dd6ff..95dbaed2dee84280a07778589b757930237c964f 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java @@ -69,6 +69,11 @@ public class Constants */ public static final String LOGIN_TOKEN_KEY = "login_tokens:"; + /** + * api 接口验证 redis key + */ + public static final String ACCESS_TOKEN = "access_token:"; + /** * 防重提交 redis key */ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/data/AccessBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/data/AccessBody.java new file mode 100644 index 0000000000000000000000000000000000000000..fac5c6bd62cb6e8db919532983d127c5d5dbbc75 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/data/AccessBody.java @@ -0,0 +1,140 @@ +package com.ruoyi.common.core.domain.entity.data; + + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +/** + * 接口访问实体 + */ +public class AccessBody { + + private Long appId; + + private Long userId; + + private String appKey; + + private String accessToken; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + public AccessBody( Long appId, Long userId, String appKey) { + this.appId = appId; + this.userId = userId; + this.appKey = appKey; + } + + public AccessBody() { + } + + public Long getLoginTime() { + return loginTime; + } + + public void setLoginTime(Long loginTime) { + this.loginTime = loginTime; + } + + public Long getExpireTime() { + return expireTime; + } + + public void setExpireTime(Long expireTime) { + this.expireTime = expireTime; + } + + public String getIpaddr() { + return ipaddr; + } + + public void setIpaddr(String ipaddr) { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) { + this.loginLocation = loginLocation; + } + + public String getBrowser() { + return browser; + } + + public void setBrowser(String browser) { + this.browser = browser; + } + + public String getOs() { + return os; + } + + public void setOs(String os) { + this.os = os; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public Long getAppId() { + return appId; + } + + public void setAppId(Long appId) { + this.appId = appId; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getAppKey() { + return appKey; + } + + public void setAppKey(String appKey) { + this.appKey = appKey; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/data/JwtAccessToken.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/data/JwtAccessToken.java new file mode 100644 index 0000000000000000000000000000000000000000..eaa98bbe7e41ab9ef409100aca9d95c260b0fc05 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/data/JwtAccessToken.java @@ -0,0 +1,29 @@ +package com.ruoyi.common.core.domain.entity.data; + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +public class JwtAccessToken extends AbstractAuthenticationToken { + + private final AccessBody accessBody; + + private final Object credentials; + + public JwtAccessToken(Collection authorities, AccessBody accessBody, Object credentials) { + super(authorities); + this.accessBody = accessBody; + this.credentials = credentials; + } + + @Override + public Object getCredentials() { + return credentials; + } + + @Override + public AccessBody getPrincipal() { + return accessBody; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/data/filter/AccessTokenConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/data/filter/AccessTokenConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..3dcee6725646785c9b82ffaa881693b0d821d944 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/data/filter/AccessTokenConfig.java @@ -0,0 +1,57 @@ +package com.ruoyi.data.filter; + + +import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.config.annotation.web.HttpSecurityBuilder; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy; + +import java.util.ArrayList; +import java.util.List; + +@Configuration +public class AccessTokenConfig , B extends HttpSecurityBuilder> + extends AbstractHttpConfigurer{ + + @Override + public void configure(B builder) throws Exception { + builder.addFilterAfter(jwtAccessTokenFilter(), LogoutFilter.class); + } + + @Bean + JwtAccessTokenFilter jwtAccessTokenFilter(){ + // 使用自定义的filter + JwtAccessTokenFilter jwtAccessTokenFilter = new JwtAccessTokenFilter(); + // 新建providerManager并为filter配置provider + List provider = new ArrayList(); + AccessTokenProvider accessTokenProvider = new AccessTokenProvider(); + provider.add(accessTokenProvider); + ProviderManager providerManager = new ProviderManager(provider); + jwtAccessTokenFilter.setAuthenticationManager(providerManager); + //注入accessTokenService +// jwtAccessTokenFilter.setAccessTokenService(accessTokenService); + jwtAccessTokenFilter.setSessionAuthenticationStrategy(new NullAuthenticatedSessionStrategy()); + jwtAccessTokenFilter.setAuthenticationSuccessHandler(accessTokenSuccessHandler()); + jwtAccessTokenFilter.setAuthenticationFailureHandler(accessTokenFailureHandler()); + + return jwtAccessTokenFilter; + } + + @Bean + AccessTokenSuccessHandler accessTokenSuccessHandler(){ + return new AccessTokenSuccessHandler(); + } + + @Bean + AccessTokenFailureHandler accessTokenFailureHandler(){ + return new AccessTokenFailureHandler(); + } + + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/data/filter/AccessTokenFailureHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/data/filter/AccessTokenFailureHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..7e4c7fe8ecaa20dba651827ab8077ac25b7fddca --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/data/filter/AccessTokenFailureHandler.java @@ -0,0 +1,24 @@ +package com.ruoyi.data.filter; + +import com.alibaba.fastjson.JSON; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class AccessTokenFailureHandler implements AuthenticationFailureHandler { + + + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { + int code = 501; + String msg = StringUtils.format("您无权访问该接口", request.getRequestURI()); + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/data/filter/AccessTokenProvider.java b/ruoyi-framework/src/main/java/com/ruoyi/data/filter/AccessTokenProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..e95bc057cdd7efa4966f6767d8eb90231ccbe50c --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/data/filter/AccessTokenProvider.java @@ -0,0 +1,28 @@ +package com.ruoyi.data.filter; + +import com.ruoyi.common.core.domain.entity.data.AccessBody; +import com.ruoyi.common.core.domain.entity.data.JwtAccessToken; +import io.jsonwebtoken.JwtException; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; + +public class AccessTokenProvider implements AuthenticationProvider { + + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + try { + JwtAccessToken jwtAccessToken = (JwtAccessToken) authentication; + return jwtAccessToken; + }catch (JwtException e){ + throw new AuthenticationServiceException("Token invalidate"); + } + } + + @Override + public boolean supports(Class aClass) { + return aClass.isAssignableFrom(JwtAccessToken.class); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/data/filter/AccessTokenService.java b/ruoyi-framework/src/main/java/com/ruoyi/data/filter/AccessTokenService.java new file mode 100644 index 0000000000000000000000000000000000000000..5515b1f1f39c6e71a0487c746468de6dd10d915b --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/data/filter/AccessTokenService.java @@ -0,0 +1,196 @@ +package com.ruoyi.data.filter; + +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.AddressUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.common.core.domain.entity.data.AccessBody; +import eu.bitwalker.useragentutils.UserAgent; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + + +@Component +public class AccessTokenService { + + // 令牌自定义标识 + @Value("${accessToken.header}") + private String header; + + // 令牌秘钥 + @Value("${accessToken.secret}") + private String secret; + + // 令牌有效期(默认30分钟) + @Value("${accessToken.expireTime}") + private int expireTime; + + + + protected static final long MILLIS_SECOND = 1000; + + protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; + + private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L; + + @Autowired + private RedisCache redisCache; + + + /** + * 创建令牌 + * + * @param accessBody + * @return 令牌 + */ + public String createAccessToken(AccessBody accessBody) + { + String token = IdUtils.fastUUID(); + accessBody.setAccessToken(token); + setAgent(accessBody); + refreshToken(accessBody); + + Map claims = new HashMap<>(); + claims.put(Constants.ACCESS_TOKEN, token); + return createToken(claims); + } + + + /** + * 设置用户代理信息 + * + * @param accessBody + */ + public void setAgent(AccessBody accessBody) + { + UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + accessBody.setIpaddr(ip); + accessBody.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + accessBody.setBrowser(userAgent.getBrowser().getName()); + accessBody.setOs(userAgent.getOperatingSystem().getName()); + } + + /** + * 刷新令牌有效期 + * + * @param accessBody + */ + public void refreshToken(AccessBody accessBody) + { + accessBody.setLoginTime(System.currentTimeMillis()); + accessBody.setExpireTime(accessBody.getLoginTime() + 30 * MILLIS_MINUTE); + // 根据uuid将loginUser缓存 + String userKey = getTokenKey(accessBody.getAccessToken()); + redisCache.setCacheObject(userKey, accessBody, 30, TimeUnit.MINUTES); + } + + private String getTokenKey(String uuid) + { + return Constants.ACCESS_TOKEN + uuid; + } + + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + private String createToken(Map claims) + { + String token = Jwts.builder() + .setClaims(claims) + .signWith(SignatureAlgorithm.HS512, secret).compact(); + return token; + } + + /** + * 从令牌中获取 + * @param request + * @return + */ + public AccessBody getAccessBody(HttpServletRequest request) + { + // 获取请求携带的令牌 + String token = getToken(request); + if (StringUtils.isNotEmpty(token)) + { + try + { + Claims claims = parseToken(token); + // 解析对应的权限以及用户信息 + String uuid = (String) claims.get(Constants.ACCESS_TOKEN); + String accessToken = getTokenKey(uuid); + AccessBody accessBody = redisCache.getCacheObject(accessToken); + return accessBody; + } + catch (Exception e) + { + } + } + return null; + } + + /** + * 获取请求acces_token + * @param request + * @return + */ + private String getToken(HttpServletRequest request) + { + try{ + String token = request.getHeader("AccessToken"); + if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) + { + token = token.replace(Constants.TOKEN_PREFIX, ""); + } + return token; + }catch (Exception e){ + return null; + } + + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * @return 数据声明 + */ + private Claims parseToken(String token) + { + return Jwts.parser() + .setSigningKey("abcdefghijklmnopqrstuvwxyz") + .parseClaimsJws(token) + .getBody(); + } + + /** + * 验证令牌有效期,相差不足20分钟,自动刷新缓存 + * + * @param accessBody + * @return 令牌 + */ + public void verifyToken(AccessBody accessBody) + { + long expireTime = accessBody.getExpireTime(); + long currentTime = System.currentTimeMillis(); + if (expireTime - currentTime <= MILLIS_MINUTE_TEN) + { + refreshToken(accessBody); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/data/filter/AccessTokenSuccessHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/data/filter/AccessTokenSuccessHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..df2359f5cf5432df04f7e683d50607fb3f10428a --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/data/filter/AccessTokenSuccessHandler.java @@ -0,0 +1,41 @@ +package com.ruoyi.data.filter; + +import com.alibaba.fastjson.JSON; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class AccessTokenSuccessHandler implements AuthenticationSuccessHandler { + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException { + if(authentication.getPrincipal() == null){ + int code = 501; + String msg = StringUtils.format("您无权访问该接口", request.getRequestURI()); + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); + }else { + chain.doFilter(request,response); + } + return; + } + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { + if(authentication.getPrincipal() == null){ + int code = 501; + String msg = StringUtils.format("您无权访问该接口", request.getRequestURI()); + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); + } + return; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/data/filter/JwtAccessTokenFilter.java b/ruoyi-framework/src/main/java/com/ruoyi/data/filter/JwtAccessTokenFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..4ac52441f7e8f440832940e3acf45ab385592abb --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/data/filter/JwtAccessTokenFilter.java @@ -0,0 +1,84 @@ +package com.ruoyi.data.filter; + +import com.ruoyi.common.core.domain.entity.data.JwtAccessToken; +import com.ruoyi.common.core.domain.entity.data.AccessBody; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; +import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher; +import org.springframework.stereotype.Component; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + + +@Component +public class JwtAccessTokenFilter extends AbstractAuthenticationProcessingFilter { + + @Autowired + public AccessTokenSuccessHandler accessTokenSuccessHandler; + + + //拦截请求头为AccessToken的地址 + public JwtAccessTokenFilter(){ + super(new RequestHeaderRequestMatcher("AccessToken")); +// super("/data/app/list"); + } + + +// @Override +// public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { +// HttpServletRequest request = (HttpServletRequest) req; +// HttpServletResponse response = (HttpServletResponse) res; +// +// JwtAccessToken jwtAccessToken; +// try { +// jwtAccessToken =(JwtAccessToken)attemptAuthentication(request, response); +// if (jwtAccessToken.getPrincipal() == null) { +// return; +// } +// } +// catch (InternalAuthenticationServiceException failed) { +// unsuccessfulAuthentication(request, response, failed); +// return; +// } +// catch (AuthenticationException failed) { +// // Authentication failed +// unsuccessfulAuthentication(request, response, failed); +// return; +// } +// +// successfulAuthentication(request,response,chain,jwtAccessToken); +// } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { + + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "text/html;charset=UTF-8"); + AccessTokenService accessTokenService = new AccessTokenService(); + AccessBody accessBody = accessTokenService.getAccessBody(request); + if(accessBody != null){ + accessTokenService.verifyToken(accessBody); + } + return getAuthenticationManager().authenticate(new JwtAccessToken(null,accessBody, accessBody == null ? null:accessBody.getAccessToken())); + } + + @Override + protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { + AccessTokenSuccessHandler accessTokenSuccessHandler = new AccessTokenSuccessHandler(); + accessTokenSuccessHandler.onAuthenticationSuccess(request,response,chain,authResult); +// chain.doFilter(request,response); + } + + @Override + protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { + + } + + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/data/filter/RewriteAccessDenyFilter.java b/ruoyi-framework/src/main/java/com/ruoyi/data/filter/RewriteAccessDenyFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..735d5935bd4d3e238955b1c47881147e65dba7ee --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/data/filter/RewriteAccessDenyFilter.java @@ -0,0 +1,27 @@ +package com.ruoyi.data.filter; + +import com.alibaba.fastjson.JSON; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + + +@Component +public class RewriteAccessDenyFilter implements AccessDeniedHandler { + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { + int code = 501; + String msg = StringUtils.format("您无权访问该接口", request.getRequestURI()); + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java index d00f00da184982eaf578fda824d97b16a03aeeb0..a77e2fdafecfde769cfa3534f6f7e8ef8e7e9b37 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -1,5 +1,8 @@ package com.ruoyi.framework.config; +import com.ruoyi.data.filter.AccessTokenConfig; +import com.ruoyi.data.filter.JwtAccessTokenFilter; +import com.ruoyi.data.filter.RewriteAccessDenyFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; @@ -40,6 +43,20 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter @Autowired private AuthenticationEntryPointImpl unauthorizedHandler; + + /** + * access_token认证过滤器 + */ + @Autowired + private JwtAccessTokenFilter jwtAccessTokenFilter; + + /** + * 自定义无权访问拦截器返回值 + */ + @Autowired + private RewriteAccessDenyFilter rewriteAccessDenyFilter; + + /** * 退出处理类 */ @@ -98,7 +115,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() // 过滤请求 .authorizeRequests() - // 对于登录login 注册register 验证码captchaImage 允许匿名访问 + // 对于登录login 注册register 验证码captchaImage 申请访问token 允许匿名访问 .antMatchers("/login", "/register", "/captchaImage").anonymous() .antMatchers( HttpMethod.GET, @@ -127,6 +144,14 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); // 添加CORS filter httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); + + //添加自定义拦截器 +// httpSecurity.addFilterAfter(jwtAccessTokenFilter, JwtAuthenticationTokenFilter.class) +// .exceptionHandling().accessDeniedHandler(rewriteAccessDenyFilter); + httpSecurity.apply(new AccessTokenConfig<>()); + + + httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); } diff --git a/ruoyi-ui/src/api/data/app.js b/ruoyi-ui/src/api/data/app.js new file mode 100644 index 0000000000000000000000000000000000000000..c8350020c8f4a7bbe583d189a8bd186a961132a8 --- /dev/null +++ b/ruoyi-ui/src/api/data/app.js @@ -0,0 +1,65 @@ +import request from '@/utils/request' +import { praseStrEmpty } from "@/utils/ruoyi"; + + +// 查询用户列表 +export function listApp(query) { + return request({ + url: '/data/app/list', + method: 'get', + params: query + }) +} + +// 申请key值 +export function applyKey(appId) { + return request({ + url: '/data/app/getKey' +"?appId="+appId , + method: 'get', + }) +} + + +// 查询应用详细 +export function getApp(id) { + return request({ + url: '/data/app/' + praseStrEmpty(id), + method: 'get' + }) +} + +// 新增应用 +export function addApp(data) { + return request({ + url: '/data/app', + method: 'post', + data: data + }) +} + +// 修改应用 +export function editApp(data) { + return request({ + url: '/data/app', + method: 'put', + data: data + }) +} + +// 删除应用 +export function delApp(id) { + return request({ + url: '/data/app/' + id, + method: 'delete' + }) +} + +// 删除应用 +export function testTest() { + return request({ + url: '/testUrl/testPrint', + method: 'get' + }) +} + + diff --git a/ruoyi-ui/src/views/data/app/index.vue b/ruoyi-ui/src/views/data/app/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..df168bd72aac71624104c67e444ad4a19ea5faf6 --- /dev/null +++ b/ruoyi-ui/src/views/data/app/index.vue @@ -0,0 +1,307 @@ + + +