Commit 684f00db by fengshuonan

升级企业版 v3.0

parent f66e431d
# Guns企业版
---
## 分支说明
| 分支名称 | 说明 | 长期支持 |
| :---: | :---: | :---: |
| maseter | 主分支,Guns企业版的核心分支,使用过程中都以master为主 | 是 |
| multi | 多数据源的分支,如果使用多数据源您可参考commit `632c91b9`来集成多数据源配置,也可以通过文档`https://www.stylefeng.cn/doc/guns#/`来查看如何集成 | 否 |
| sso | 集成DCA单点登录服务器的示例,您也可以通过单点登录SSO相关的文档查看如何集成`https://www.stylefeng.cn/doc/sso` | 否 |
**长期支持说明:**长期支持分支是作者长期更新的分支,推荐您的项目都基于长期支持分支之上来做,其他分支是为了实现某些功能的临时分支,不建议项目基于此分支开发。
## 项目模块介绍
为了方便项目之后的升级,现在把项目拆分成了四个模块,guns-base,guns-sys-guns-vip-gen不建议您在此基础上修改上面的任何内容,您修改后可能会升级后出现问题,您的业务代码尽量都在guns-vip-main,或者新建模块进行开发
| 模块名称 | 说明 |
| :---: | :---: |
| guns-base | guns的基础模块 |
| guns-base-email | 邮件发送模块 |
| guns-base-sms | 短信发送模块(对接阿里云短信) |
| guns-base-timers | 分布式任务调度执行器 |
| guns-sys | guns系统管理的基础业务模块 |
| guns-vip-gen | guns代码生成器模块 |
| guns-vip-main | 主启动模块,也是您的业务的编写的地方 |
# Guns最新企业版
\ No newline at end of file
/* 这个sql用在测试多数据源的时候,可以不用运行 */
DROP DATABASE IF EXISTS guns_test_db;
CREATE DATABASE IF NOT EXISTS guns_test_db DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
USE guns_test_db;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`USER_ID` bigint(20) NOT NULL COMMENT '主键id',
`AVATAR` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '头像',
`ACCOUNT` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '账号',
`PASSWORD` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '密码',
`SALT` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT 'md5密码盐',
`NAME` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '名字',
`BIRTHDAY` datetime(0) DEFAULT NULL COMMENT '生日',
`SEX` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '性别(字典)',
`EMAIL` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '电子邮件',
`PHONE` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '电话',
`ROLE_ID` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '角色id(多个逗号隔开)',
`DEPT_ID` bigint(20) DEFAULT NULL COMMENT '部门id(多个逗号隔开)',
`STATUS` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '状态(字典)',
`CREATE_TIME` datetime(0) DEFAULT NULL COMMENT '创建时间',
`CREATE_USER` bigint(20) DEFAULT NULL COMMENT '创建人',
`UPDATE_TIME` datetime(0) DEFAULT NULL COMMENT '更新时间',
`UPDATE_USER` bigint(20) DEFAULT NULL COMMENT '更新人',
`VERSION` int(11) DEFAULT NULL COMMENT '乐观锁',
PRIMARY KEY (`USER_ID`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '管理员表' ROW_FORMAT = Dynamic;
\ No newline at end of file
Guns默认用mysql作为主要数据源!
Guns默认用mysql作为主要数据源!
其他数据库会定期适配!
oracle最近适配日期:2019.07.16
pgsql最近适配日期:2019.04.08
sql server最近适配日期:2019.08.26
\ No newline at end of file
......@@ -11,7 +11,7 @@
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>guns-rest-api</artifactId>
<artifactId>guns-base-auth</artifactId>
<packaging>jar</packaging>
......@@ -24,6 +24,12 @@
<version>1.0.0</version>
</dependency>
<!-- security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<build>
......
......@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.base.shiro.annotion;
package cn.stylefeng.guns.base.auth.annotion;
import java.lang.annotation.*;
......
......@@ -13,10 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.sys.core.shiro.aop;
package cn.stylefeng.guns.base.auth.aop;
import cn.stylefeng.guns.base.shiro.annotion.Permission;
import cn.stylefeng.guns.sys.core.shiro.service.PermissionCheckService;
import cn.stylefeng.guns.base.auth.annotion.Permission;
import cn.stylefeng.guns.base.auth.exception.PermissionException;
import cn.stylefeng.guns.base.auth.service.AuthService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
......@@ -24,9 +25,7 @@ import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.naming.NoPermissionException;
import java.lang.reflect.Method;
/**
......@@ -36,14 +35,13 @@ import java.lang.reflect.Method;
* @date 2017-07-13 21:05
*/
@Aspect
@Component
@Order(200)
public class PermissionAop {
@Autowired
private PermissionCheckService check;
private AuthService authService;
@Pointcut(value = "@annotation(cn.stylefeng.guns.base.shiro.annotion.Permission)")
@Pointcut(value = "@annotation(cn.stylefeng.guns.base.auth.annotion.Permission)")
private void cutPermission() {
}
......@@ -53,25 +51,25 @@ public class PermissionAop {
MethodSignature ms = (MethodSignature) point.getSignature();
Method method = ms.getMethod();
Permission permission = method.getAnnotation(Permission.class);
Object[] permissions = permission.value();
String[] permissions = permission.value();
if (permissions.length == 0) {
//检查全体角色
boolean result = check.checkAll();
boolean result = authService.checkAll();
if (result) {
return point.proceed();
} else {
throw new NoPermissionException();
throw new PermissionException();
}
} else {
//检查指定角色
boolean result = check.check(permissions);
boolean result = authService.check(permissions);
if (result) {
return point.proceed();
} else {
throw new NoPermissionException();
throw new PermissionException();
}
}
}
......
package cn.stylefeng.guns.base.auth.context;
import cn.stylefeng.guns.base.auth.model.LoginUser;
import java.util.List;
/**
* 当前登录用户信息获取的接口
*
* @author fengshuonan
* @Date 2019/7/18 22:27
*/
public interface LoginContext {
/**
* 获取当前登录用户
*
* @author fengshuonan
* @Date 2019/7/18 22:31
*/
LoginUser getUser();
/**
* 获取当前登录用户的token
*
* @author fengshuonan
* @Date 2019/7/18 22:31
*/
String getToken();
/**
* 是否登录
*
* @author fengshuonan
* @Date 2019/7/18 22:31
*/
boolean hasLogin();
/**
* 获取当前登录用户id
*
* @author fengshuonan
* @Date 2019/7/18 22:31
*/
Long getUserId();
/**
* 验证当前用户是否包含该角色
*
* @param roleName 角色名称
* @return 包含:true, 否则false
*/
boolean hasRole(String roleName);
/**
* 验证当前用户是否属于以下任意一个角色
*
* @param roleNames 角色列表,逗号分隔
* @return 包含:true, 否则false
*/
boolean hasAnyRoles(String roleNames);
/**
* 验证当前用户是否拥有指定权限
*
* @param permission 权限名
* @return 拥有权限:true,否则false
*/
boolean hasPermission(String permission);
/**
* 判断当前用户是否是超级管理员
*/
boolean isAdmin();
/**
* 判断用户是否是从oauth2登录过来的
*/
boolean oauth2Flag();
/**
* 获取当前用户的部门数据范围的集合
*/
List<Long> getDeptDataScope();
}
package cn.stylefeng.guns.base.auth.context;
import cn.stylefeng.roses.core.util.SpringContextHolder;
/**
* 当前登录用户信息获取的接口
*
* @author fengshuonan
* @Date 2019/7/18 22:27
*/
public class LoginContextHolder {
public static LoginContext getContext() {
return SpringContextHolder.getBean(LoginContext.class);
}
}
package cn.stylefeng.guns.base.auth.exception;
import cn.stylefeng.roses.kernel.model.exception.AbstractBaseExceptionEnum;
import lombok.Data;
/**
* 认证失败(账号密码错误,账号被冻结,token过期等)
*
* @author fengshuonan
* @Date 2019/7/18 22:18
*/
@Data
public class AuthException extends RuntimeException {
private Integer code;
private String errorMessage;
public AuthException() {
super("认证失败!");
this.code = 500;
this.errorMessage = "认证失败!";
}
public AuthException(AbstractBaseExceptionEnum exception) {
super(exception.getMessage());
this.code = exception.getCode();
this.errorMessage = exception.getMessage();
}
}
package cn.stylefeng.guns.base.auth.exception;
import cn.stylefeng.roses.kernel.model.exception.AbstractBaseExceptionEnum;
import lombok.Data;
import static cn.stylefeng.guns.base.auth.exception.enums.AuthExceptionEnum.NO_PERMISSION;
/**
* 没有访问权限
*
* @author fengshuonan
* @Date 2019/7/18 22:18
*/
@Data
public class PermissionException extends RuntimeException {
private Integer code;
private String errorMessage;
public PermissionException() {
super(NO_PERMISSION.getMessage());
this.code = NO_PERMISSION.getCode();
this.errorMessage = NO_PERMISSION.getMessage();
}
public PermissionException(AbstractBaseExceptionEnum exception) {
super(exception.getMessage());
this.code = exception.getCode();
this.errorMessage = exception.getMessage();
}
}
package cn.stylefeng.guns.base.auth.exception.enums;
import cn.stylefeng.roses.kernel.model.exception.AbstractBaseExceptionEnum;
import lombok.Getter;
/**
* 认证失败的异常枚举
*
* @author fengshuonan
* @Date 2019/7/18 22:22
*/
@Getter
public enum AuthExceptionEnum implements AbstractBaseExceptionEnum {
NOT_LOGIN_ERROR(1401, "用户未登录"),
USERNAME_PWD_ERROR(1402, "账号密码错误"),
LOGIN_EXPPIRED(1403, "登录已过期,请重新登录"),
ACCOUNT_FREEZE_ERROR(1404, "账号被冻结"),
NO_ROLE_ERROR(1405, "用户没有分配角色,获取菜单失败"),
VALID_CODE_ERROR(1406, "验证码错误"),
NO_PERMISSION(1500, "没有权限访问资源");
AuthExceptionEnum(int code, String message) {
this.code = code;
this.message = message;
}
private Integer code;
private String message;
}
/**
* Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)
* Copyright 2018-2020 stylefeng & fengshuonan (sn93@qq.com)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.api.core.util;
package cn.stylefeng.guns.base.auth.jwt;
import cn.stylefeng.guns.api.core.constant.JwtConstants;
import cn.stylefeng.guns.base.auth.jwt.payload.JwtPayLoad;
import cn.stylefeng.guns.base.consts.ConstantsContext;
import cn.stylefeng.roses.core.util.ToolUtil;
import io.jsonwebtoken.*;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
......@@ -42,62 +42,37 @@ import java.util.Map;
public class JwtTokenUtil {
/**
* 获取用户名从token中
* 生成token,根据userId和默认过期时间
*/
public static String getUsernameFromToken(String token) {
return getClaimFromToken(token).getSubject();
}
/**
* 获取jwt发布时间
*/
public static Date getIssuedAtDateFromToken(String token) {
return getClaimFromToken(token).getIssuedAt();
}
/**
* 获取jwt失效时间
*/
public static Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token).getExpiration();
}
/**
* 获取jwt接收者
*/
public static String getAudienceFromToken(String token) {
return getClaimFromToken(token).getAudience();
}
/**
* 获取私有的jwt claim
*/
public static String getPrivateClaimFromToken(String token, String key) {
return getClaimFromToken(token).get(key).toString();
public static String generateToken(JwtPayLoad jwtPayLoad) {
Long expiredSeconds = getExpireSeconds();
final Date expirationDate = new Date(System.currentTimeMillis() + expiredSeconds * 1000);
return generateToken(String.valueOf(jwtPayLoad.getUserId()), expirationDate, jwtPayLoad.toMap());
}
/**
* 获取jwt的payload部分
*/
public static Claims getClaimFromToken(String token) {
return Jwts.parser()
.setSigningKey(JwtConstants.SECRET)
.parseClaimsJws(token)
.getBody();
public static JwtPayLoad getJwtPayLoad(String token) {
Claims claimFromToken = getClaimFromToken(token);
return JwtPayLoad.toBean(claimFromToken);
}
/**
* 解析token是否正确,不正确会报异常<br>
* 解析token是否正确(true-正确, false-错误)
*/
public static void parseToken(String token) throws JwtException {
Jwts.parser().setSigningKey(JwtConstants.SECRET).parseClaimsJws(token).getBody();
public static Boolean checkToken(String token) {
try {
String jwtSecret = getJwtSecret();
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody();
return true;
} catch (JwtException e) {
return false;
}
}
/**
* <pre>
* 验证token是否失效
* true:过期 false:没过期
* </pre>
* 验证token是否失效
*/
public static Boolean isTokenExpired(String token) {
try {
......@@ -109,33 +84,59 @@ public class JwtTokenUtil {
}
/**
* 生成token(通过用户名和签名时候用的随机数)
* 获取jwt失效时间
*/
public static String generateToken(String userId) {
Map<String, Object> claims = new HashMap<>();
return doGenerateToken(claims, userId);
public static Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token).getExpiration();
}
/**
* 生成token
* 生成token,根据userId和过期时间
*/
private static String doGenerateToken(Map<String, Object> claims, String subject) {
public static String generateToken(String userId, Date exppiredDate, Map<String, Object> claims) {
final Date createdDate = new Date();
final Date expirationDate = new Date(createdDate.getTime() + JwtConstants.EXPIRATION * 1000);
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(createdDate)
.setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS512, JwtConstants.SECRET)
.compact();
String secret = getJwtSecret();
if (claims == null) {
return Jwts.builder()
.setSubject(userId)
.setIssuedAt(createdDate)
.setExpiration(exppiredDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
} else {
return Jwts.builder()
.setClaims(claims)
.setSubject(userId)
.setIssuedAt(createdDate)
.setExpiration(exppiredDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
}
/**
* 获取混淆MD5签名用的随机字符串
* 获取jwt的payload部分
*/
public static String getRandomKey() {
return ToolUtil.getRandomString(6);
public static Claims getClaimFromToken(String token) {
if (ToolUtil.isEmpty(token)) {
throw new IllegalArgumentException("token参数为空!");
}
String jwtSecret = getJwtSecret();
return Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
}
private static String getJwtSecret() {
return ConstantsContext.getJwtSecret();
}
private static Long getExpireSeconds() {
return ConstantsContext.getJwtSecretExpireSec();
}
}
\ No newline at end of file
package cn.stylefeng.guns.base.auth.jwt.payload;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* jwt的第二部分
*
* @author fengshuonan
* @Date 2019/7/20 20:45
*/
@Data
public class JwtPayLoad {
/**
* 用户id
*/
private Long userId;
/**
* 账号
*/
private String account;
/**
* 用户的键
*/
private String userKey;
public JwtPayLoad() {
}
public JwtPayLoad(Long userId, String account, String userKey) {
this.userId = userId;
this.account = account;
this.userKey = userKey;
}
/**
* payload转化为map形式
*
* @author fengshuonan
* @Date 2019/7/20 20:50
*/
public Map<String, Object> toMap() {
HashMap<String, Object> map = new HashMap<>();
map.put("userId", this.userId);
map.put("account", this.account);
map.put("userKey", this.userKey);
return map;
}
/**
* payload转化为map形式
*
* @author fengshuonan
* @Date 2019/7/20 20:50
*/
public static JwtPayLoad toBean(Map<String, Object> map) {
if (map == null || map.size() == 0) {
return new JwtPayLoad();
} else {
JwtPayLoad jwtPayLoad = new JwtPayLoad();
Object userId = map.get("userId");
if (userId instanceof Integer) {
jwtPayLoad.setUserId(Long.valueOf(map.get("userId").toString()));
}
jwtPayLoad.setAccount((String) map.get("account"));
jwtPayLoad.setUserKey((String) map.get("userKey"));
return jwtPayLoad;
}
}
}
......@@ -13,22 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.base.shiro;
package cn.stylefeng.guns.base.auth.model;
import cn.stylefeng.roses.core.util.ToolUtil;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* 自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息
* 当前登录用户信息
*
* @author fengshuonan
* @date 2016年12月5日 上午10:26:43
* @Date 2019/7/18 22:29
*/
@Data
public class ShiroUser implements Serializable {
public class LoginUser implements UserDetails, Serializable {
private static final long serialVersionUID = 1L;
......@@ -78,8 +80,73 @@ public class ShiroUser implements Serializable {
private List<String> roleNames;
/**
* 角色备注(code)
*/
private List<String> roleTips;
/**
* 系统标识集合
*/
private List<Map<String, Object>> systemTypes;
/**
* 拥有的权限
*/
private Set<String> permissions;
/**
* 租户编码
*/
private String tenantCode;
/**
* 租户的数据源名称
*/
private String tenantDataSourceName;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
ArrayList<GrantedAuthority> grantedAuthorities = new ArrayList<>();
if (ToolUtil.isNotEmpty(this.roleNames)) {
for (String roleName : this.roleNames) {
GrantedAuthority grantedAuthority = (GrantedAuthority) () -> roleName;
grantedAuthorities.add(grantedAuthority);
}
}
return grantedAuthorities;
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return this.account;
}
@Override
public boolean isAccountNonExpired() {
//能生成loginUser就是jwt解析成功,没锁定
return true;
}
@Override
public boolean isAccountNonLocked() {
//能生成loginUser就是jwt解析成功,没锁定
return true;
}
@Override
public boolean isCredentialsNonExpired() {
//能生成loginUser就是jwt解析成功,没锁定
return true;
}
@Override
public boolean isEnabled() {
//能生成loginUser就是jwt解析成功,没锁定
return true;
}
}
......@@ -13,35 +13,58 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.sys.core.shiro.service;
package cn.stylefeng.guns.base.auth.service;
import cn.stylefeng.guns.base.shiro.ShiroUser;
import cn.stylefeng.guns.sys.modular.system.entity.User;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import cn.stylefeng.guns.base.auth.model.LoginUser;
import java.util.List;
/**
* 定义shirorealm所需数据的接口
* Auth相关数据库的操作
*
* @author fengshuonan
* @date 2016年12月5日 上午10:23:34
*/
public interface UserAuthService {
public interface AuthService {
/**
* 根据账号获取登录用户
* 登录
*
* @param account 账号
* @param username 账号
* @param password 密码
* @return token
*/
String login(String username, String password);
/**
* 登录(直接用账号登录)
*
* @param username 账号
* @return token
*/
String login(String username);
/**
* 创建登录cookie
*/
User user(String account);
void addLoginCookie(String token);
/**
* 根据系统用户获取Shiro的用户
* 退出当前用户
*/
void logout();
/**
* 退出
*/
void logout(String token);
/**
* 根据账号获取登录用户
*
* @param user 系统用户
* @param account 账号
*/
ShiroUser shiroUser(User user);
LoginUser user(String account);
/**
* 获取权限列表通过角色id
......@@ -51,15 +74,15 @@ public interface UserAuthService {
List<String> findPermissionsByRoleId(Long roleId);
/**
* 根据角色id获取角色名称
* 检查当前登录用户是否拥有指定的角色访问当
*
* @param roleId 角色id
* @param roleNames 角色名称集合
*/
String findRoleNameByRoleId(Long roleId);
boolean check(String[] roleNames);
/**
* 获取shiro的认证信息
* 检查当前登录用户是否拥有当前请求的servlet的权限
*/
SimpleAuthenticationInfo info(ShiroUser shiroUser, User user, String realmName);
boolean checkAll();
}
......@@ -64,56 +64,12 @@
<scope>test</scope>
</dependency>
<!--shiro依赖和缓存-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<!--beetl模板引擎-->
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<!--jwt token-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
......@@ -156,16 +112,6 @@
<artifactId>oshi-core</artifactId>
</dependency>
<!--需要分布式session的话需要放开注释-->
<!--<dependency>-->
<!--<groupId>org.springframework.session</groupId>-->
<!--<artifactId>spring-session-data-redis</artifactId>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.boot</groupId>-->
<!--<artifactId>spring-boot-starter-data-redis</artifactId>-->
<!--</dependency>-->
</dependencies>
<build>
......
......@@ -5,10 +5,12 @@ import cn.stylefeng.guns.base.sms.AliyunSmsProperties;
import cn.stylefeng.roses.core.util.ToolUtil;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static cn.stylefeng.guns.base.consts.ConfigConstant.SYSTEM_CONSTANT_PREFIX;
import static cn.stylefeng.roses.core.util.ToolUtil.getTempPath;
/**
* 系统常量的容器
......@@ -19,6 +21,8 @@ import static cn.stylefeng.guns.base.consts.ConfigConstant.SYSTEM_CONSTANT_PREFI
@Slf4j
public class ConstantsContext {
private static final String TIPS_END = ",若想忽略此提示,请在开发管理->系统配置->参数配置,设置相关参数!";
/**
* 所有的常量,可以增删改查
*/
......@@ -92,7 +96,7 @@ public class ConstantsContext {
public static String getSystemName() {
String systemName = (String) CONSTNTS_HOLDER.get("GUNS_SYSTEM_NAME");
if (ToolUtil.isEmpty(systemName)) {
log.error("系统常量存在空值!常量名称:GUNS_SYSTEM_NAME,采用默认名称:Guns快速开发平台");
log.error("系统常量存在空值!常量名称:GUNS_SYSTEM_NAME,采用默认名称:Guns快速开发平台" + TIPS_END);
return "Guns快速开发平台";
} else {
return systemName;
......@@ -105,7 +109,7 @@ public class ConstantsContext {
public static String getDefaultPassword() {
String defaultPassword = (String) CONSTNTS_HOLDER.get("GUNS_DEFAULT_PASSWORD");
if (ToolUtil.isEmpty(defaultPassword)) {
log.error("系统常量存在空值!常量名称:GUNS_DEFAULT_PASSWORD,采用默认密码:111111");
log.error("系统常量存在空值!常量名称:GUNS_DEFAULT_PASSWORD,采用默认密码:111111" + TIPS_END);
return "111111";
} else {
return defaultPassword;
......@@ -118,7 +122,7 @@ public class ConstantsContext {
public static String getOAuth2UserPrefix() {
String oauth2Prefix = (String) CONSTNTS_HOLDER.get("GUNS_OAUTH2_PREFIX");
if (ToolUtil.isEmpty(oauth2Prefix)) {
log.error("系统常量存在空值!常量名称:GUNS_OAUTH2_PREFIX,采用默认值:oauth2");
log.error("系统常量存在空值!常量名称:GUNS_OAUTH2_PREFIX,采用默认值:oauth2" + TIPS_END);
return "oauth2";
} else {
return oauth2Prefix;
......@@ -131,7 +135,7 @@ public class ConstantsContext {
public static Boolean getDefaultAdvert() {
String gunsDefaultAdvert = (String) CONSTNTS_HOLDER.get("GUNS_DEFAULT_ADVERT");
if (ToolUtil.isEmpty(gunsDefaultAdvert)) {
log.error("系统常量存在空值!常量名称:GUNS_DEFAULT_ADVERT,采用默认值:true");
log.error("系统常量存在空值!常量名称:GUNS_DEFAULT_ADVERT,采用默认值:true" + TIPS_END);
return true;
} else {
if (CommonStatus.ENABLE.getCode().equalsIgnoreCase(gunsDefaultAdvert)) {
......@@ -148,10 +152,135 @@ public class ConstantsContext {
public static String getReleaseVersion() {
String systemReleaseVersion = (String) CONSTNTS_HOLDER.get("GUNS_SYSTEM_RELEASE_VERSION");
if (ToolUtil.isEmpty(systemReleaseVersion)) {
log.error("系统常量存在空值!常量名称:systemReleaseVersion,采用默认值:guns");
log.error("系统常量存在空值!常量名称:GUNS_SYSTEM_RELEASE_VERSION,采用默认值:guns" + TIPS_END);
return ToolUtil.getRandomString(8);
} else {
return systemReleaseVersion;
}
}
/**
* 获取文件上传路径(用于头像和富文本编辑器)
*/
public static String getFileUploadPath() {
String gunsFileUploadPath = (String) CONSTNTS_HOLDER.get("GUNS_FILE_UPLOAD_PATH");
if (ToolUtil.isEmpty(gunsFileUploadPath)) {
log.error("系统常量存在空值!常量名称:GUNS_FILE_UPLOAD_PATH,采用默认值:系统tmp目录" + TIPS_END);
return getTempPath();
} else {
//判断有没有结尾符
if (!gunsFileUploadPath.endsWith(File.separator)) {
gunsFileUploadPath = gunsFileUploadPath + File.separator;
}
//判断目录存不存在
File file = new File(gunsFileUploadPath);
if (!file.exists()) {
boolean mkdirs = file.mkdirs();
if (mkdirs) {
return gunsFileUploadPath;
} else {
log.error("系统常量存在空值!常量名称:GUNS_FILE_UPLOAD_PATH,采用默认值:系统tmp目录" + TIPS_END);
return getTempPath();
}
} else {
return gunsFileUploadPath;
}
}
}
/**
* 用于存放bpmn文件
*/
public static String getBpmnFileUploadPath() {
String bpmnFileUploadPath = (String) CONSTNTS_HOLDER.get("GUNS_BPMN_FILE_UPLOAD_PATH");
if (ToolUtil.isEmpty(bpmnFileUploadPath)) {
log.error("系统常量存在空值!常量名称:GUNS_BPMN_FILE_UPLOAD_PATH,采用默认值:系统tmp目录" + TIPS_END);
return getTempPath();
} else {
//判断有没有结尾符
if (!bpmnFileUploadPath.endsWith(File.separator)) {
bpmnFileUploadPath = bpmnFileUploadPath + File.separator;
}
//判断目录存不存在
File file = new File(bpmnFileUploadPath);
if (!file.exists()) {
boolean mkdirs = file.mkdirs();
if (mkdirs) {
return bpmnFileUploadPath;
} else {
log.error("系统常量存在空值!常量名称:GUNS_BPMN_FILE_UPLOAD_PATH,采用默认值:系统tmp目录" + TIPS_END);
return getTempPath();
}
} else {
return bpmnFileUploadPath;
}
}
}
/**
* 获取系统地密钥
*/
public static String getJwtSecret() {
String systemReleaseVersion = (String) CONSTNTS_HOLDER.get("GUNS_JWT_SECRET");
if (ToolUtil.isEmpty(systemReleaseVersion)) {
String randomSecret = ToolUtil.getRandomString(32);
CONSTNTS_HOLDER.put("GUNS_JWT_SECRET", randomSecret);
log.error("jwt密钥存在空值!常量名称:GUNS_JWT_SECRET,采用默认值:随机字符串->" + randomSecret + TIPS_END);
return randomSecret;
} else {
return systemReleaseVersion;
}
}
/**
* 获取系统地密钥过期时间(单位:秒)
*/
public static Long getJwtSecretExpireSec() {
Long defaultSecs = 86400L;
String systemReleaseVersion = (String) CONSTNTS_HOLDER.get("GUNS_JWT_SECRET_EXPIRE");
if (ToolUtil.isEmpty(systemReleaseVersion)) {
log.error("jwt密钥存在空值!常量名称:GUNS_JWT_SECRET_EXPIRE,采用默认值:1天" + TIPS_END);
CONSTNTS_HOLDER.put("GUNS_JWT_SECRET_EXPIRE", String.valueOf(defaultSecs));
return defaultSecs;
} else {
try {
return Long.valueOf(systemReleaseVersion);
} catch (NumberFormatException e) {
log.error("jwt密钥过期时间不是数字!常量名称:GUNS_JWT_SECRET_EXPIRE,采用默认值:1天" + TIPS_END);
CONSTNTS_HOLDER.put("GUNS_JWT_SECRET_EXPIRE", String.valueOf(defaultSecs));
return defaultSecs;
}
}
}
/**
* 获取token的header标识
*/
public static String getTokenHeaderName() {
String tokenHeaderName = (String) CONSTNTS_HOLDER.get("GUNS_TOKEN_HEADER_NAME");
if (ToolUtil.isEmpty(tokenHeaderName)) {
String defaultName = "Authorization";
CONSTNTS_HOLDER.put("GUNS_TOKEN_HEADER_NAME", defaultName);
log.error("获取token的header标识为空!常量名称:GUNS_TOKEN_HEADER_NAME,采用默认值:" + defaultName + TIPS_END);
return defaultName;
} else {
return tokenHeaderName;
}
}
/**
* 获取租户是否开启的标识,默认是关的
*/
public static Boolean getTenantOpen() {
String tenantOpen = (String) CONSTNTS_HOLDER.get("GUNS_TENANT_OPEN");
if (ToolUtil.isEmpty(tenantOpen)) {
log.error("系统常量存在空值!常量名称:GUNS_TENANT_OPEN,采用默认值:DISABLE" + TIPS_END);
return false;
} else {
return CommonStatus.ENABLE.getCode().equalsIgnoreCase(tenantOpen);
}
}
}
......@@ -27,6 +27,11 @@ public class DataSourceContext {
private static Map<String, DataSource> DATA_SOURCES = new ConcurrentHashMap<>();
/**
* 数据源的配置容器
*/
private static Map<String, DruidProperties> DATA_SOURCES_CONF = new ConcurrentHashMap<>();
/**
* 初始化所有dataSource
*
* @author fengshuonan
......@@ -44,6 +49,9 @@ public class DataSourceContext {
DataBaseInfoDao dataBaseInfoDao = new DataBaseInfoDao(masterDataSourceProperties);
Map<String, DruidProperties> allDataBaseInfo = dataBaseInfoDao.getAllDataBaseInfo();
//赋给全局变量
DATA_SOURCES_CONF = allDataBaseInfo;
//根据数据源信息初始化所有的DataSource
for (Map.Entry<String, DruidProperties> entry : allDataBaseInfo.entrySet()) {
......@@ -77,9 +85,23 @@ public class DataSourceContext {
}
/**
* 获取数据源的配置
*
* @author fengshuonan
* @Date 2019-06-18 19:26
*/
public static Map<String, DruidProperties> getDataSourcesConfs() {
return DATA_SOURCES_CONF;
}
/**
* 数据源创建模板
*/
public static DataSource createDataSource(String dataSourceName, DruidProperties druidProperties) {
//添加到全局配置里
DATA_SOURCES_CONF.put(dataSourceName, druidProperties);
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
atomikosDataSourceBean.setUniqueResourceName(dataSourceName);
......
package cn.stylefeng.guns.base.db.util;
import cn.stylefeng.guns.base.db.entity.DatabaseInfo;
import cn.stylefeng.roses.core.config.properties.DruidProperties;
import lombok.extern.slf4j.Slf4j;
import java.sql.Connection;
......@@ -33,11 +34,11 @@ public class DbUtil {
Class.forName(dbInfo.getJdbcDriver());
Connection conn = DriverManager.getConnection(dbInfo.getJdbcUrl(), dbInfo.getUserName(), dbInfo.getPassword());
String jdbcUrl = dbInfo.getJdbcUrl();
int first = jdbcUrl.lastIndexOf("/") + 1;
int last = jdbcUrl.indexOf("?");
String dbName = jdbcUrl.substring(first, last);
PreparedStatement preparedStatement = conn.prepareStatement("select TABLE_NAME as tableName,TABLE_COMMENT as tableComment from information_schema.`TABLES` where TABLE_SCHEMA = '" + dbName + "'");
//获取数据库名称
String dbName = getDbName(dbInfo);
PreparedStatement preparedStatement = conn.prepareStatement(
"select TABLE_NAME as tableName,TABLE_COMMENT as tableComment from information_schema.`TABLES` where TABLE_SCHEMA = '" + dbName + "'");
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
HashMap<String, Object> map = new HashMap<>();
......@@ -66,10 +67,9 @@ public class DbUtil {
Class.forName(dbInfo.getJdbcDriver());
Connection conn = DriverManager.getConnection(dbInfo.getJdbcUrl(), dbInfo.getUserName(), dbInfo.getPassword());
String jdbcUrl = dbInfo.getJdbcUrl();
int first = jdbcUrl.lastIndexOf("/") + 1;
int last = jdbcUrl.indexOf("?");
String dbName = jdbcUrl.substring(first, last);
//获取数据库名称
String dbName = getDbName(dbInfo);
PreparedStatement preparedStatement = conn.prepareStatement(
"select COLUMN_NAME as columnName,COLUMN_COMMENT as columnComment from information_schema.COLUMNS where table_name = '" + tableName + "' and table_schema = '" + dbName + "'");
ResultSet resultSet = preparedStatement.executeQuery();
......@@ -88,4 +88,39 @@ public class DbUtil {
}
}
/**
* 创建数据库
*
* @author fengshuonan
* @Date 2019-06-18 15:29
*/
public static void createDatabase(DruidProperties druidProperties, String databaseName) {
try {
Class.forName(druidProperties.getDriverClassName());
Connection conn = DriverManager.getConnection(druidProperties.getUrl(), druidProperties.getUsername(), druidProperties.getPassword());
//创建sql
String sql = "CREATE DATABASE IF NOT EXISTS " + databaseName + " DEFAULT CHARSET utf8 COLLATE utf8_general_ci;";
PreparedStatement preparedStatement = conn.prepareStatement(sql);
int i = preparedStatement.executeUpdate();
log.info("创建数据库!数量:" + i);
} catch (Exception ex) {
log.error("执行sql出现问题!", ex);
}
}
/**
* 获取数据库名称
*
* @author fengshuonan
* @Date 2019-06-18 15:25
*/
private static String getDbName(DatabaseInfo dbInfo) {
String jdbcUrl = dbInfo.getJdbcUrl();
int first = jdbcUrl.lastIndexOf("/") + 1;
int last = jdbcUrl.indexOf("?");
return jdbcUrl.substring(first, last);
}
}
package cn.stylefeng.guns.base.db.util;
import cn.stylefeng.guns.base.db.context.DataSourceContext;
import cn.stylefeng.guns.base.db.context.SqlSessionFactoryContext;
import cn.stylefeng.roses.core.config.properties.DruidProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.jdbc.datasource.init.ScriptUtils;
import java.sql.Connection;
import java.sql.DriverManager;
/**
* sql文件执行
*
* @author fengshuonan
* @date 2019-06-18-17:10
*/
@Slf4j
public class SqlRunUtil {
/**
* 执行sql脚本文件,使用Spring工具类
*/
public static void runClassPathSql(String classpathFileName, String dbName) {
DruidProperties druidProperties = DataSourceContext.getDataSourcesConfs().get(dbName);
try {
Class.forName(druidProperties.getDriverClassName());
Connection conn = DriverManager.getConnection(druidProperties.getUrl(), druidProperties.getUsername(), druidProperties.getPassword());
ClassPathResource classPathResource = new ClassPathResource(classpathFileName);
EncodedResource encodedResource = new EncodedResource(classPathResource, "utf-8");
ScriptUtils.executeSqlScript(conn, encodedResource);
} catch (Exception e) {
log.error("执行sql错误!", e);
throw new RuntimeException("执行sql错误!");
}
}
/**
* 执行系统路径sql的文件
*/
public static void runFileSystemSql(String dbName, String sqlPath) {
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryContext.getSqlSessionFactorys().get(dbName);
try {
SqlSession sqlSession = sqlSessionFactory.openSession();
Connection conn = sqlSession.getConnection();
FileSystemResource classPathResource = new FileSystemResource(sqlPath);
EncodedResource encodedResource = new EncodedResource(classPathResource, "GBK");
ScriptUtils.executeSqlScript(conn, encodedResource);
} catch (Exception e) {
log.error("执行sql错误!", e);
throw new RuntimeException("执行sql错误!");
}
}
}
......@@ -145,7 +145,13 @@ public class SystemHardwareInfo {
sysFile.setTotal(convertFileSize(total));
sysFile.setFree(convertFileSize(free));
sysFile.setUsed(convertFileSize(used));
sysFile.setUsage(NumberUtil.mul(NumberUtil.div(used, total, 4), 100));
if (total == 0) {
sysFile.setUsage(0);
} else {
sysFile.setUsage(NumberUtil.mul(NumberUtil.div(used, total, 4), 100));
}
sysFiles.add(sysFile);
}
}
......
package cn.stylefeng.guns.base.pojo.node;
import cn.stylefeng.roses.core.util.ToolUtil;
import cn.stylefeng.roses.kernel.model.tree.Tree;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* layui属性组件节点
*
* @author stylefeng
* @Date 2019-8-26 14:01
*/
@Data
public class LayuiTreeNode implements Tree {
/**
* 节点id
*/
private Long id;
/**
* 父级节点id
*/
private Long pid;
/**
* 节点名称
*/
private String title;
/**
* 节点是否初始展开
*/
private Boolean spread;
/**
* 节点是否初始为选中状态
*/
private Boolean checked;
/**
* 节点是否为禁用状态
*/
private Boolean disabled;
private List<LayuiTreeNode> children = new ArrayList<>();
@Override
public String getNodeId() {
if (ToolUtil.isNotEmpty(id)) {
return String.valueOf(id);
} else {
return null;
}
}
@Override
public String getNodeParentId() {
if (ToolUtil.isNotEmpty(pid)) {
return String.valueOf(pid);
} else {
return null;
}
}
@Override
public void setChildrenNodes(List childrenNodes) {
this.children = childrenNodes;
}
}
......@@ -35,6 +35,11 @@ public class MenuNode implements Comparable, Serializable {
private Long id;
/**
* 菜单编码
*/
private String code;
/**
* 父节点
*/
private Long parentId;
......
......@@ -16,6 +16,7 @@
package cn.stylefeng.guns.base.pojo.page;
import cn.stylefeng.roses.core.util.HttpContext;
import cn.stylefeng.roses.core.util.ToolUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
......@@ -38,11 +39,20 @@ public class LayuiPageFactory {
public static Page defaultPage() {
HttpServletRequest request = HttpContext.getRequest();
int limit = 20;
int page = 1;
//每页多少条数据
int limit = Integer.valueOf(request.getParameter("limit"));
String limitString = request.getParameter("limit");
if (ToolUtil.isNotEmpty(limitString)) {
limit = Integer.parseInt(limitString);
}
//第几页
int page = Integer.valueOf(request.getParameter("page"));
String pageString = request.getParameter("page");
if (ToolUtil.isNotEmpty(pageString)) {
page = Integer.parseInt(pageString);
}
return new Page(page, limit);
}
......
/**
* Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.api.core.aop;
import cn.stylefeng.guns.api.core.constant.JwtConstants;
import cn.stylefeng.guns.api.core.exception.RestExceptionEnum;
import cn.stylefeng.guns.api.core.util.JwtTokenUtil;
import cn.stylefeng.roses.core.reqres.response.ErrorResponseData;
import cn.stylefeng.roses.core.util.RenderUtil;
import io.jsonwebtoken.JwtException;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Rest Api接口鉴权
*
* @author stylefeng
* @Date 2018/7/20 23:11
*/
public class RestApiInteceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof org.springframework.web.servlet.resource.ResourceHttpRequestHandler) {
return true;
}
return check(request, response);
}
private boolean check(HttpServletRequest request, HttpServletResponse response) {
if (request.getServletPath().equals(JwtConstants.AUTH_PATH)) {
return true;
}
final String requestHeader = request.getHeader(JwtConstants.AUTH_HEADER);
String authToken;
if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
authToken = requestHeader.substring(7);
//验证token是否过期,包含了验证jwt是否正确
try {
boolean flag = JwtTokenUtil.isTokenExpired(authToken);
if (flag) {
RenderUtil.renderJson(response, new ErrorResponseData(
RestExceptionEnum.TOKEN_EXPIRED.getCode(), RestExceptionEnum.TOKEN_EXPIRED.getMessage()));
return false;
}
} catch (JwtException e) {
//有异常就是token解析失败
RenderUtil.renderJson(response, new ErrorResponseData(
RestExceptionEnum.TOKEN_ERROR.getCode(), RestExceptionEnum.TOKEN_ERROR.getMessage()));
return false;
}
} else {
//header没有带Bearer字段
RenderUtil.renderJson(response, new ErrorResponseData(
RestExceptionEnum.TOKEN_ERROR.getCode(), RestExceptionEnum.TOKEN_ERROR.getMessage()));
return false;
}
return true;
}
}
/**
* Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.api.core.exception;
import cn.stylefeng.roses.kernel.model.exception.AbstractBaseExceptionEnum;
/**
* rest异常
*
* @author fengshuonan
* @date 2016年11月12日 下午5:04:51
*/
public enum RestExceptionEnum implements AbstractBaseExceptionEnum {
/**
* token异常
*/
TOKEN_EXPIRED(700, "token过期"),
TOKEN_ERROR(700, "token验证失败"),
/**
* 签名异常
*/
SIGN_ERROR(700, "签名验证失败");
RestExceptionEnum(int code, String message) {
this.code = code;
this.message = message;
}
private Integer code;
private String message;
@Override
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
......@@ -24,10 +24,10 @@
<version>1.0.0</version>
</dependency>
<!-- rest-api-->
<!-- 权限管理模块 -->
<dependency>
<groupId>cn.stylefeng</groupId>
<artifactId>guns-rest-api</artifactId>
<artifactId>guns-base-auth</artifactId>
<version>1.0.0</version>
</dependency>
......@@ -42,6 +42,28 @@
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<!--百度UeEditor-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
</dependencies>
......
/**
* Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.sys.core.attribute;
import cn.stylefeng.guns.base.shiro.ShiroUser;
import cn.stylefeng.guns.sys.core.shiro.ShiroKit;
import cn.stylefeng.guns.sys.core.util.DefaultImages;
import org.apache.shiro.authc.AuthenticationException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 自动渲染当前用户信息登录属性 的过滤器
*
* @author fengshuonan
* @Date 2018/10/30 4:30 PM
*/
public class AttributeSetInteceptor extends HandlerInterceptorAdapter {
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//没有视图的直接跳过过滤器
if (modelAndView == null || modelAndView.getViewName() == null) {
return;
}
//视图结尾不是html的直接跳过
if (!modelAndView.getViewName().endsWith("html")) {
return;
}
ShiroUser user = ShiroKit.getUser();
if (user == null) {
throw new AuthenticationException("当前没有登录账号!");
} else {
modelAndView.addObject("name", user.getName());
modelAndView.addObject("avatar", DefaultImages.defaultAvatarUrl());
modelAndView.addObject("email", user.getEmail());
}
}
}
/**
* Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.sys.core.auth;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.stylefeng.guns.base.auth.context.LoginContextHolder;
import cn.stylefeng.guns.base.auth.exception.AuthException;
import cn.stylefeng.guns.base.auth.exception.enums.AuthExceptionEnum;
import cn.stylefeng.guns.base.auth.jwt.JwtTokenUtil;
import cn.stylefeng.guns.base.auth.jwt.payload.JwtPayLoad;
import cn.stylefeng.guns.base.auth.model.LoginUser;
import cn.stylefeng.guns.base.auth.service.AuthService;
import cn.stylefeng.guns.sys.core.auth.cache.SessionManager;
import cn.stylefeng.guns.sys.core.constant.factory.ConstantFactory;
import cn.stylefeng.guns.sys.core.constant.state.ManagerStatus;
import cn.stylefeng.guns.sys.core.listener.ConfigListener;
import cn.stylefeng.guns.sys.core.log.LogManager;
import cn.stylefeng.guns.sys.core.log.factory.LogTaskFactory;
import cn.stylefeng.guns.sys.core.util.SaltUtil;
import cn.stylefeng.guns.sys.modular.system.entity.User;
import cn.stylefeng.guns.sys.modular.system.factory.UserFactory;
import cn.stylefeng.guns.sys.modular.system.mapper.MenuMapper;
import cn.stylefeng.guns.sys.modular.system.mapper.UserMapper;
import cn.stylefeng.guns.sys.modular.system.service.DictService;
import cn.stylefeng.roses.core.util.HttpContext;
import cn.stylefeng.roses.core.util.SpringContextHolder;
import cn.stylefeng.roses.core.util.ToolUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import static cn.stylefeng.guns.base.consts.ConstantsContext.getJwtSecretExpireSec;
import static cn.stylefeng.guns.base.consts.ConstantsContext.getTokenHeaderName;
import static cn.stylefeng.roses.core.util.HttpContext.getIp;
@Service
@DependsOn("springContextHolder")
@Transactional(readOnly = true)
public class AuthServiceImpl implements AuthService {
@Autowired
private UserMapper userMapper;
@Autowired
private MenuMapper menuMapper;
@Autowired
private DictService dictService;
@Autowired
private SessionManager sessionManager;
public static AuthService me() {
return SpringContextHolder.getBean(AuthService.class);
}
@Override
public String login(String username, String password) {
User user = userMapper.getByAccount(username);
// 账号不存在
if (null == user) {
throw new AuthException(AuthExceptionEnum.USERNAME_PWD_ERROR);
}
//验证账号密码是否正确
String requestMd5 = SaltUtil.md5Encrypt(password, user.getSalt());
String dbMd5 = user.getPassword();
if (dbMd5 == null || !dbMd5.equalsIgnoreCase(requestMd5)) {
throw new AuthException(AuthExceptionEnum.USERNAME_PWD_ERROR);
}
return login(username);
}
@Override
public String login(String username) {
User user = userMapper.getByAccount(username);
// 账号不存在
if (null == user) {
throw new AuthException(AuthExceptionEnum.USERNAME_PWD_ERROR);
}
// 账号被冻结
if (!user.getStatus().equals(ManagerStatus.OK.getCode())) {
throw new AuthException(AuthExceptionEnum.ACCOUNT_FREEZE_ERROR);
}
//记录登录日志
LogManager.me().executeLog(LogTaskFactory.loginLog(user.getUserId(), getIp()));
//TODO key的作用
JwtPayLoad payLoad = new JwtPayLoad(user.getUserId(), user.getAccount(), "xxxx");
//创建token
String token = JwtTokenUtil.generateToken(payLoad);
//创建登录会话
sessionManager.createSession(token, user(username));
//创建cookie
addLoginCookie(token);
return token;
}
@Override
public void addLoginCookie(String token) {
//创建cookie
Cookie authorization = new Cookie(getTokenHeaderName(), token);
authorization.setMaxAge(getJwtSecretExpireSec().intValue());
authorization.setHttpOnly(true);
authorization.setPath("/");
HttpServletResponse response = HttpContext.getResponse();
response.addCookie(authorization);
}
@Override
public void logout() {
String token = LoginContextHolder.getContext().getToken();
logout(token);
}
@Override
public void logout(String token) {
//记录退出日志
LogManager.me().executeLog(LogTaskFactory.exitLog(LoginContextHolder.getContext().getUser().getId(), getIp()));
//删除Auth相关cookies
Cookie[] cookies = HttpContext.getRequest().getCookies();
for (Cookie cookie : cookies) {
String tokenHeader = getTokenHeaderName();
if (tokenHeader.equalsIgnoreCase(cookie.getName())) {
Cookie temp = new Cookie(cookie.getName(), "");
temp.setMaxAge(0);
HttpContext.getResponse().addCookie(temp);
}
}
//删除会话
sessionManager.removeSession(token);
}
@Override
public LoginUser user(String account) {
User user = userMapper.getByAccount(account);
LoginUser loginUser = UserFactory.createLoginUser(user);
//用户角色数组
Long[] roleArray = Convert.toLongArray(user.getRoleId());
//如果角色是空就直接返回
if (roleArray == null || roleArray.length == 0) {
return loginUser;
}
//获取用户角色列表
List<Long> roleList = new ArrayList<>();
List<String> roleNameList = new ArrayList<>();
List<String> roleTipList = new ArrayList<>();
for (Long roleId : roleArray) {
roleList.add(roleId);
roleNameList.add(ConstantFactory.me().getSingleRoleName(roleId));
roleTipList.add(ConstantFactory.me().getSingleRoleTip(roleId));
}
loginUser.setRoleList(roleList);
loginUser.setRoleNames(roleNameList);
loginUser.setRoleTips(roleTipList);
//根据角色获取系统的类型
List<String> systemTypes = this.menuMapper.getMenusTypesByRoleIds(roleList);
//通过字典编码
List<Map<String, Object>> dictsByCodes = dictService.getDictsByCodes(systemTypes);
loginUser.setSystemTypes(dictsByCodes);
//设置权限列表
Set<String> permissionSet = new HashSet<>();
for (Long roleId : roleList) {
List<String> permissions = this.findPermissionsByRoleId(roleId);
if (permissions != null) {
for (String permission : permissions) {
if (ToolUtil.isNotEmpty(permission)) {
permissionSet.add(permission);
}
}
}
}
loginUser.setPermissions(permissionSet);
return loginUser;
}
@Override
public List<String> findPermissionsByRoleId(Long roleId) {
return menuMapper.getResUrlsByRoleId(roleId);
}
@Override
public boolean check(String[] roleNames) {
LoginUser user = LoginContextHolder.getContext().getUser();
if (null == user) {
return false;
}
ArrayList<String> objects = CollectionUtil.newArrayList(roleNames);
String join = CollectionUtil.join(objects, ",");
if (LoginContextHolder.getContext().hasAnyRoles(join)) {
return true;
}
return false;
}
@Override
public boolean checkAll() {
HttpServletRequest request = HttpContext.getRequest();
LoginUser user = LoginContextHolder.getContext().getUser();
if (null == user) {
return false;
}
String requestURI = request.getRequestURI().replaceFirst(ConfigListener.getConf().get("contextPath"), "");
String[] str = requestURI.split("/");
if (str.length > 3) {
requestURI = "/" + str[1] + "/" + str[2];
}
if (LoginContextHolder.getContext().hasPermission(requestURI)) {
return true;
}
return false;
}
}
package cn.stylefeng.guns.sys.core.auth;
import cn.stylefeng.guns.base.auth.context.LoginContext;
import cn.stylefeng.guns.base.auth.exception.AuthException;
import cn.stylefeng.guns.base.auth.exception.enums.AuthExceptionEnum;
import cn.stylefeng.guns.base.auth.model.LoginUser;
import cn.stylefeng.guns.base.consts.ConstantsContext;
import cn.stylefeng.guns.sys.core.constant.Const;
import cn.stylefeng.guns.sys.core.constant.factory.ConstantFactory;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 用户登录上下文
*
* @author fengshuonan
* @Date 2019/7/18 22:27
*/
@Component
public class LoginContextSpringSecutiryImpl implements LoginContext {
@Override
public LoginUser getUser() {
if (SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof String) {
throw new AuthException(AuthExceptionEnum.NOT_LOGIN_ERROR);
} else {
return (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
}
@Override
public String getToken() {
return null;
}
@Override
public boolean hasLogin() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return false;
} else {
if (authentication instanceof AnonymousAuthenticationToken) {
return false;
} else {
return true;
}
}
}
@Override
public Long getUserId() {
return getUser().getId();
}
@Override
public boolean hasRole(String roleName) {
return getUser().getRoleTips().contains(roleName);
}
@Override
public boolean hasAnyRoles(String roleNames) {
boolean hasAnyRole = false;
if (this.hasLogin() && roleNames != null && roleNames.length() > 0) {
for (String role : roleNames.split(",")) {
if (hasRole(role.trim())) {
hasAnyRole = true;
break;
}
}
}
return hasAnyRole;
}
@Override
public boolean hasPermission(String permission) {
return getUser().getPermissions().contains(permission);
}
@Override
public boolean isAdmin() {
List<Long> roleList = getUser().getRoleList();
for (Long integer : roleList) {
String singleRoleTip = ConstantFactory.me().getSingleRoleTip(integer);
if (singleRoleTip.equals(Const.ADMIN_NAME)) {
return true;
}
}
return false;
}
@Override
public boolean oauth2Flag() {
String account = getUser().getAccount();
if (account.startsWith(ConstantsContext.getOAuth2UserPrefix())) {
return true;
} else {
return false;
}
}
@Override
public List<Long> getDeptDataScope() {
Long deptId = getUser().getDeptId();
List<Long> subDeptIds = ConstantFactory.me().getSubDeptId(deptId);
subDeptIds.add(deptId);
return subDeptIds;
}
}
package cn.stylefeng.guns.sys.core.auth.cache;
import cn.stylefeng.guns.base.auth.model.LoginUser;
/**
* 会话管理
*
* @author fengshuonan
* @date 2019-09-28-14:43
*/
public interface SessionManager {
/**
* 缓存前缀
*/
String SESSION_PREFIX = "LOGIN_USER_";
/**
* 创建会话
*
* @author fengshuonan
* @Date 2019-09-28 14:50
*/
void createSession(String token, LoginUser loginUser);
/**
* 获取会话
*
* @author fengshuonan
* @Date 2019-09-28 14:50
*/
LoginUser getSession(String token);
/**
* 删除会话
*
* @author fengshuonan
* @Date 2019-09-28 14:50
*/
void removeSession(String token);
/**
* 是否已经登陆
*
* @author fengshuonan
* @Date 2019-09-28 14:56
*/
boolean haveSession(String token);
}
package cn.stylefeng.guns.sys.core.auth.cache.impl;
import cn.stylefeng.guns.base.auth.model.LoginUser;
import cn.stylefeng.guns.sys.core.auth.cache.SessionManager;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 基于内存的会话管理
*
* @author fengshuonan
* @date 2019-09-28-14:43
*/
@Component
public class DefaultSessionManager implements SessionManager {
private Map<String, LoginUser> caches = new ConcurrentHashMap<>();
@Override
public void createSession(String token, LoginUser loginUser) {
caches.put(SESSION_PREFIX + token, loginUser);
}
@Override
public LoginUser getSession(String token) {
return caches.get(SESSION_PREFIX + token);
}
@Override
public void removeSession(String token) {
caches.remove(SESSION_PREFIX + token);
}
@Override
public boolean haveSession(String token) {
return caches.containsKey(SESSION_PREFIX + token);
}
}
package cn.stylefeng.guns.sys.core.auth.cache.impl;
import cn.stylefeng.guns.base.auth.model.LoginUser;
import cn.stylefeng.guns.sys.core.auth.cache.SessionManager;
/**
* 基于redis的会话管理
*
* @author fengshuonan
* @date 2019-09-28-14:43
*/
public class RedisSessionManager implements SessionManager {
@Override
public void createSession(String token, LoginUser loginUser) {
}
@Override
public LoginUser getSession(String token) {
return null;
}
@Override
public void removeSession(String token) {
}
@Override
public boolean haveSession(String token) {
return false;
}
}
package cn.stylefeng.guns.sys.core.auth.entrypoint;
import cn.stylefeng.guns.base.auth.exception.enums.AuthExceptionEnum;
import cn.stylefeng.roses.core.reqres.response.ErrorResponseData;
import com.alibaba.fastjson.JSON;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Serializable;
/**
* 这个端点用在用户访问受保护资源但是不提供任何token的情况下
*
* @author fengshuonan
* @Date 2019/7/20 17:57
*/
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = -1L;
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
// GET请求跳转到主页
if ("get".equalsIgnoreCase(request.getMethod())
&& !request.getHeader("Accept").contains("application/json")) {
response.sendRedirect(request.getContextPath() + "/global/sessionError");
} else {
// POST请求返回json
response.setCharacterEncoding("utf-8");
response.setContentType("application/json");
ErrorResponseData errorResponseData = new ErrorResponseData(
AuthExceptionEnum.NO_PERMISSION.getCode(), AuthExceptionEnum.NO_PERMISSION.getMessage());
response.getWriter().write(JSON.toJSONString(errorResponseData));
}
}
}
\ No newline at end of file
package cn.stylefeng.guns.sys.core.auth.filter;
import cn.stylefeng.guns.base.auth.jwt.JwtTokenUtil;
import cn.stylefeng.guns.sys.core.auth.cache.SessionManager;
import cn.stylefeng.roses.core.util.ToolUtil;
import io.jsonwebtoken.JwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import static cn.stylefeng.guns.base.consts.ConstantsContext.getTokenHeaderName;
/**
* jwt token过滤器
*
* @author fengshuonan
* @Date 2019/7/20 21:33
*/
@Component
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {
@Autowired
private SessionManager sessionManager;
public JwtAuthorizationTokenFilter() {
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
//过滤静态资源
String[] regs = {"/assets/**", "/favicon.ico", "/activiti-editor/**"};
for (String reg : regs) {
if (new AntPathMatcher().match(reg, request.getServletPath())) {
chain.doFilter(request, response);
return;
}
}
//权限校验的头部
String tokenHeader = getTokenHeaderName();
final String requestHeader = request.getHeader(tokenHeader);
String username = null;
String authToken = null;
// 1.从cookie中获取token
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (tokenHeader.equals(cookie.getName())) {
authToken = cookie.getValue();
}
}
}
// 2.如果cookie中没有token,则从header中获取token
if (ToolUtil.isEmpty(authToken)) {
if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
authToken = requestHeader.substring(7);
}
}
// 3.通过token获取用户名
if (ToolUtil.isNotEmpty(authToken)) {
try {
username = JwtTokenUtil.getJwtPayLoad(authToken).getAccount();
} catch (IllegalArgumentException | JwtException e) {
//请求token为空或者token不正确,忽略,并不是所有接口都要鉴权
}
}
// 4.如果账号不为空,并且没有设置security上下文,就设置上下文
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
//从缓存中拿userDetails
UserDetails userDetails = sessionManager.getSession(authToken);
if (userDetails == null) {
//删除cookies
Cookie[] tempCookies = request.getCookies();
for (Cookie cookie : tempCookies) {
if (tokenHeader.equals(cookie.getName())) {
Cookie temp = new Cookie(cookie.getName(), "");
temp.setMaxAge(0);
response.addCookie(temp);
}
}
//跳转到登录超时
request.getRequestDispatcher("/global/sessionError").forward(request, response);
return;
}
//创建当前登录上下文
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
}
package cn.stylefeng.guns.sys.core.auth.userdetail;
import cn.stylefeng.guns.base.auth.model.LoginUser;
import cn.stylefeng.guns.base.auth.service.AuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
/**
* 用户详情信息获取
*
* @author fengshuonan
* @Date 2019-09-28 14:07
*/
@Service("jwtUserDetailsService")
public class JwtUserDetailsServiceImpl implements UserDetailsService {
@Autowired
private AuthService authService;
@Override
public LoginUser loadUserByUsername(String username) throws UsernameNotFoundException {
return authService.user(username);
}
}
......@@ -15,14 +15,11 @@
*/
package cn.stylefeng.guns.sys.core.beetl;
import cn.stylefeng.guns.base.auth.context.LoginContext;
import cn.stylefeng.guns.base.consts.ConstantsContext;
import cn.stylefeng.guns.sys.core.util.KaptchaUtil;
import cn.stylefeng.roses.core.util.ToolUtil;
import org.beetl.ext.spring.BeetlGroupUtilConfiguration;
import java.util.HashMap;
import java.util.Map;
/**
* beetl拓展配置,绑定一些工具类,方便在模板中直接调用
*
......@@ -31,13 +28,16 @@ import java.util.Map;
*/
public class BeetlConfiguration extends BeetlGroupUtilConfiguration {
private LoginContext loginContext;
public BeetlConfiguration(LoginContext loginContext) {
this.loginContext = loginContext;
}
@Override
public void initOther() {
//全局共享方法
groupTemplate.registerFunctionPackage("shiro", new ShiroExt());
groupTemplate.registerFunctionPackage("shiro", loginContext);
groupTemplate.registerFunctionPackage("tool", new ToolUtil());
groupTemplate.registerFunctionPackage("kaptcha", new KaptchaUtil());
groupTemplate.registerFunctionPackage("constants", new ConstantsContext());
}
}
/**
* Copyright (c) 2015-2017, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.sys.core.beetl;
import cn.stylefeng.guns.base.shiro.ShiroUser;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
public class ShiroExt {
private static final String NAMES_DELIMETER = ",";
/**
* 获取当前 Subject
*
* @return Subject
*/
public static Subject getSubject() {
return SecurityUtils.getSubject();
}
/**
* 获取封装的 ShiroUser
*
* @return ShiroUser
*/
public ShiroUser getUser() {
if (isGuest()) {
return null;
} else {
return (ShiroUser) getSubject().getPrincipals().getPrimaryPrincipal();
}
}
/**
* 验证当前用户是否属于该角色?,使用时与lacksRole 搭配使用
*
* @param roleName 角色名
* @return 属于该角色:true,否则false
*/
public boolean hasRole(String roleName) {
return getSubject() != null && roleName != null
&& roleName.length() > 0 && getSubject().hasRole(roleName);
}
/**
* 与hasRole标签逻辑相反,当用户不属于该角色时验证通过。
*
* @param roleName 角色名
* @return 不属于该角色:true,否则false
*/
public boolean lacksRole(String roleName) {
return !hasRole(roleName);
}
/**
* 验证当前用户是否属于以下任意一个角色。
*
* @param roleNames 角色列表
* @return 属于:true,否则false
*/
public boolean hasAnyRoles(String roleNames) {
boolean hasAnyRole = false;
Subject subject = getSubject();
if (subject != null && roleNames != null && roleNames.length() > 0) {
for (String role : roleNames.split(NAMES_DELIMETER)) {
if (subject.hasRole(role.trim())) {
hasAnyRole = true;
break;
}
}
}
return hasAnyRole;
}
/**
* 验证当前用户是否属于以下所有角色。
*
* @param roleNames 角色列表
* @return 属于:true,否则false
*/
public boolean hasAllRoles(String roleNames) {
boolean hasAllRole = true;
Subject subject = getSubject();
if (subject != null && roleNames != null && roleNames.length() > 0) {
for (String role : roleNames.split(NAMES_DELIMETER)) {
if (!subject.hasRole(role.trim())) {
hasAllRole = false;
break;
}
}
}
return hasAllRole;
}
/**
* 验证当前用户是否拥有指定权限,使用时与lacksPermission 搭配使用
*
* @param permission 权限名
* @return 拥有权限:true,否则false
*/
public boolean hasPermission(String permission) {
return getSubject() != null && permission != null
&& permission.length() > 0
&& getSubject().isPermitted(permission);
}
/**
* 与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过。
*
* @param permission 权限名
* @return 拥有权限:true,否则false
*/
public boolean lacksPermission(String permission) {
return !hasPermission(permission);
}
/**
* 已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。与notAuthenticated搭配使用
*
* @return 通过身份验证:true,否则false
*/
public boolean authenticated() {
return getSubject() != null && getSubject().isAuthenticated();
}
/**
* 未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。。
*
* @return 没有通过身份验证:true,否则false
*/
public boolean notAuthenticated() {
return !authenticated();
}
/**
* 认证通过或已记住的用户。与guset搭配使用。
*
* @return 用户:true,否则 false
*/
public boolean isUser() {
return getSubject() != null && getSubject().getPrincipal() != null;
}
/**
* 验证当前用户是否为“访客”,即未认证(包含未记住)的用户。用user搭配使用
*
* @return 访客:true,否则false
*/
public boolean isGuest() {
return !isUser();
}
/**
* 输出当前用户信息,通常为登录帐号信息。
*
* @return 当前用户信息
*/
public String principal() {
if (getSubject() != null) {
Object principal = getSubject().getPrincipal();
return principal.toString();
}
return "";
}
}
......@@ -269,6 +269,22 @@ public class ConstantFactory implements IConstantFactory {
}
@Override
public String getDictNameByCode(String dictCode) {
if (ToolUtil.isEmpty(dictCode)) {
return "";
} else {
QueryWrapper<Dict> dictQueryWrapper = new QueryWrapper<>();
dictQueryWrapper.eq("code", dictCode);
Dict dict = dictMapper.selectOne(dictQueryWrapper);
if (dict == null) {
return "";
} else {
return dict.getName();
}
}
}
@Override
public String getSexName(String sexCode) {
return getDictsByName("性别", sexCode);
}
......@@ -353,5 +369,22 @@ public class ConstantFactory implements IConstantFactory {
}
@Override
public String getPositionIds(Long userId) {
StringBuilder positionIds = new StringBuilder();
List<UserPos> userPosList = this.userPosService.list(
new QueryWrapper<UserPos>().eq("user_id", userId));
if (userPosList != null && userPosList.size() > 0) {
for (UserPos userPos : userPosList) {
Position position = positionService.getById(userPos.getPosId());
if (position != null) {
positionIds.append(",").append(position.getPositionId());
}
}
}
return StrUtil.removePrefix(positionIds.toString(), ",");
}
}
......@@ -105,6 +105,11 @@ public interface IConstantFactory {
String getDictsByName(String name, String code);
/**
* 获取字典名称
*/
String getDictNameByCode(String dictCode);
/**
* 获取性别名称
*/
String getSexName(String sexCode);
......@@ -144,4 +149,9 @@ public interface IConstantFactory {
*/
String getPositionName(Long userId);
/**
* 获取用户的职位ids
*/
String getPositionIds(Long userId);
}
......@@ -15,28 +15,25 @@
*/
package cn.stylefeng.guns.sys.core.exception.aop;
import cn.stylefeng.guns.base.auth.context.LoginContextHolder;
import cn.stylefeng.guns.base.auth.exception.AuthException;
import cn.stylefeng.guns.base.auth.exception.PermissionException;
import cn.stylefeng.guns.base.auth.exception.enums.AuthExceptionEnum;
import cn.stylefeng.guns.sys.core.exception.InvalidKaptchaException;
import cn.stylefeng.guns.sys.core.exception.enums.BizExceptionEnum;
import cn.stylefeng.guns.sys.core.log.LogManager;
import cn.stylefeng.guns.sys.core.log.factory.LogTaskFactory;
import cn.stylefeng.guns.sys.core.shiro.ShiroKit;
import cn.stylefeng.roses.core.reqres.response.ErrorResponseData;
import cn.stylefeng.roses.kernel.model.exception.ServiceException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.CredentialsException;
import org.apache.shiro.authc.DisabledAccountException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.lang.reflect.UndeclaredThrowableException;
import static cn.stylefeng.roses.core.util.HttpContext.getIp;
import static cn.stylefeng.roses.core.util.HttpContext.getRequest;
......@@ -47,82 +44,56 @@ import static cn.stylefeng.roses.core.util.HttpContext.getRequest;
* @date 2016年11月12日 下午3:19:56
*/
@ControllerAdvice
@Order(-1)
@Order(-100)
public class GlobalExceptionHandler {
private Logger log = LoggerFactory.getLogger(this.getClass());
/**
* 拦截业务异常
* 认证异常--认证失败(账号密码错误,账号被冻结,token过期等)
*/
@ExceptionHandler(ServiceException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(AuthException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ResponseBody
public ErrorResponseData bussiness(ServiceException e) {
if (ShiroKit.isUser()) {
LogManager.me().executeLog(LogTaskFactory.exceptionLog(ShiroKit.getUserNotNull().getId(), e));
}
getRequest().setAttribute("tip", e.getMessage());
log.error("业务异常:", e);
public ErrorResponseData unAuth(AuthException e) {
return new ErrorResponseData(e.getCode(), e.getMessage());
}
/**
* 用户未登录异常
* 认证异常--没有访问权限
*/
@ExceptionHandler(AuthenticationException.class)
@ExceptionHandler(PermissionException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public String unAuth(AuthenticationException e) {
log.error("用户未登陆:", e);
return "/login.html";
}
/**
* 账号被冻结异常
*/
@ExceptionHandler(DisabledAccountException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public String accountLocked(DisabledAccountException e, Model model) {
String username = getRequest().getParameter("username");
LogManager.me().executeLog(LogTaskFactory.loginLog(username, "账号被冻结", getIp()));
model.addAttribute("tips", "账号被冻结");
return "/login.html";
}
/**
* 账号密码错误异常
*/
@ExceptionHandler(CredentialsException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public String credentials(CredentialsException e, Model model) {
String username = getRequest().getParameter("username");
LogManager.me().executeLog(LogTaskFactory.loginLog(username, "账号密码错误", getIp()));
model.addAttribute("tips", "账号密码错误");
return "/login.html";
@ResponseBody
public ErrorResponseData permissionExpection(PermissionException e) {
return new ErrorResponseData(e.getCode(), e.getMessage());
}
/**
* 验证码错误异常
* 认证异常--验证码错误异常
*/
@ExceptionHandler(InvalidKaptchaException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String credentials(InvalidKaptchaException e, Model model) {
@ResponseBody
public ErrorResponseData credentials(InvalidKaptchaException e) {
String username = getRequest().getParameter("username");
LogManager.me().executeLog(LogTaskFactory.loginLog(username, "验证码错误", getIp()));
model.addAttribute("tips", "验证码错误");
return "/login.html";
return new ErrorResponseData(AuthExceptionEnum.VALID_CODE_ERROR.getCode(), AuthExceptionEnum.VALID_CODE_ERROR.getMessage());
}
/**
* 无权访问该资源异常
* 拦截业务异常
*/
@ExceptionHandler(UndeclaredThrowableException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ExceptionHandler(ServiceException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ErrorResponseData credentials(UndeclaredThrowableException e) {
getRequest().setAttribute("tip", "权限异常");
log.error("权限异常!", e);
return new ErrorResponseData(BizExceptionEnum.NO_PERMITION.getCode(), BizExceptionEnum.NO_PERMITION.getMessage());
public ErrorResponseData bussiness(ServiceException e) {
if (LoginContextHolder.getContext().hasLogin()) {
LogManager.me().executeLog(LogTaskFactory.exceptionLog(LoginContextHolder.getContext().getUserId(), e));
}
getRequest().setAttribute("tip", e.getMessage());
log.error("业务异常:", e);
return new ErrorResponseData(e.getCode(), e.getMessage());
}
/**
......@@ -132,8 +103,8 @@ public class GlobalExceptionHandler {
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ErrorResponseData notFount(RuntimeException e) {
if (ShiroKit.isUser()) {
LogManager.me().executeLog(LogTaskFactory.exceptionLog(ShiroKit.getUserNotNull().getId(), e));
if (LoginContextHolder.getContext().hasLogin()) {
LogManager.me().executeLog(LogTaskFactory.exceptionLog(LoginContextHolder.getContext().getUserId(), e));
}
getRequest().setAttribute("tip", "服务器未知运行时异常");
log.error("运行时异常:", e);
......
......@@ -91,7 +91,26 @@ public enum BizExceptionEnum implements AbstractBaseExceptionEnum {
/**
* 其他
*/
AUTH_REQUEST_ERROR(400, "账号密码错误");
AUTH_REQUEST_ERROR(400, "账号密码错误"),
/**
* ueditor相关异常
*/
UE_CONFIG_ERROR(800, "读取ueditor配置失败"),
UE_FILE_NULL_ERROR(801, "上传文件为空"),
UE_FILE_READ_ERROR(803, "读取文件错误"),
UE_FILE_SAVE_ERROR(802, "保存ue的上传文件出错"),
/**
* 工作流相关
*/
ACT_NO_FLOW(900, "无可用流程,请先导入或新建流程"),
ACT_ADD_ERROR(901, "新建流程错误"),
/**
* 租户相关的异常
*/
NO_TENANT_ERROR(1901, "没有相关租户");
BizExceptionEnum(int code, String message) {
this.code = code;
......
......@@ -46,7 +46,7 @@ public class GlobalController {
*/
@RequestMapping(path = "/sessionError")
public String errorPageInfo(Model model) {
model.addAttribute("tips", "session超时");
model.addAttribute("tips", "登陆超时,请您重新登陆!");
return "/login.html";
}
}
/**
* Copyright (c) 2015-2017, Chill Zhuang 庄骞 (smallchill@163.com).
* Copyright 2018-2020 stylefeng & fengshuonan (sn93@qq.com)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......
......@@ -15,13 +15,13 @@
*/
package cn.stylefeng.guns.sys.core.log.aop;
import cn.stylefeng.guns.base.auth.context.LoginContextHolder;
import cn.stylefeng.guns.base.auth.model.LoginUser;
import cn.stylefeng.guns.base.dict.AbstractDictMap;
import cn.stylefeng.guns.base.log.BussinessLog;
import cn.stylefeng.guns.base.shiro.ShiroUser;
import cn.stylefeng.guns.sys.core.log.LogManager;
import cn.stylefeng.guns.sys.core.log.LogObjectHolder;
import cn.stylefeng.guns.sys.core.log.factory.LogTaskFactory;
import cn.stylefeng.guns.sys.core.shiro.ShiroKit;
import cn.stylefeng.guns.sys.core.util.Contrast;
import cn.stylefeng.roses.core.util.HttpContext;
import org.aspectj.lang.ProceedingJoinPoint;
......@@ -82,7 +82,7 @@ public class LogAop {
String methodName = currentMethod.getName();
//如果当前用户未登录,不做日志
ShiroUser user = ShiroKit.getUser();
LoginUser user = LoginContextHolder.getContext().getUser();
if (null == user) {
return;
}
......
......@@ -15,10 +15,10 @@
*/
package cn.stylefeng.guns.sys.core.log.factory;
import cn.stylefeng.guns.sys.modular.system.entity.LoginLog;
import cn.stylefeng.guns.sys.modular.system.entity.OperationLog;
import cn.stylefeng.guns.sys.core.constant.state.LogSucceed;
import cn.stylefeng.guns.sys.core.constant.state.LogType;
import cn.stylefeng.guns.sys.modular.system.entity.LoginLog;
import cn.stylefeng.guns.sys.modular.system.entity.OperationLog;
import java.util.Date;
......
......@@ -15,11 +15,11 @@
*/
package cn.stylefeng.guns.sys.core.log.factory;
import cn.stylefeng.guns.sys.core.constant.state.LogSucceed;
import cn.stylefeng.guns.sys.core.constant.state.LogType;
import cn.stylefeng.guns.sys.core.log.LogManager;
import cn.stylefeng.guns.sys.modular.system.entity.LoginLog;
import cn.stylefeng.guns.sys.modular.system.entity.OperationLog;
import cn.stylefeng.guns.sys.core.constant.state.LogSucceed;
import cn.stylefeng.guns.sys.core.constant.state.LogType;
import cn.stylefeng.guns.sys.modular.system.mapper.LoginLogMapper;
import cn.stylefeng.guns.sys.modular.system.mapper.OperationLogMapper;
import cn.stylefeng.roses.core.util.SpringContextHolder;
......
......@@ -15,13 +15,8 @@
*/
package cn.stylefeng.guns.sys.core.properties;
import cn.stylefeng.roses.core.util.ToolUtil;
import lombok.Data;
import java.io.File;
import static cn.stylefeng.roses.core.util.ToolUtil.getTempPath;
/**
* guns项目配置
*
......@@ -33,10 +28,6 @@ public class GunsProperties {
public static final String PREFIX = "guns";
private String fileUploadPath;
private Boolean haveCreatePath = false;
private Boolean springSessionOpen = false;
/**
......@@ -49,26 +40,4 @@ public class GunsProperties {
*/
private Integer sessionValidationInterval = 15 * 60;
public String getFileUploadPath() {
//如果没有写文件上传路径,保存到临时目录
if (ToolUtil.isEmpty(fileUploadPath)) {
return getTempPath();
} else {
//判断有没有结尾符,没有得加上
if (!fileUploadPath.endsWith(File.separator)) {
fileUploadPath = fileUploadPath + File.separator;
}
//判断目录存不存在,不存在得加上
if (!haveCreatePath) {
File file = new File(fileUploadPath);
file.mkdirs();
haveCreatePath = true;
}
return fileUploadPath;
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package cn.stylefeng.guns.sys.core.shiro;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Filter that allows access to resources if the accessor is a known user, which is defined as
* having a known principal. This means that any user who is authenticated or remembered via a
* 'remember me' feature will be allowed access from this filter.
* <p/>
* If the accessor is not a known user, then they will be redirected to the {@link #setLoginUrl(String) loginUrl}</p>
*
* @since 0.9
*/
public class GunsUserFilter extends AccessControlFilter {
/**
* Returns <code>true</code> if the request is a
* {@link #isLoginRequest(ServletRequest, ServletResponse) loginRequest} or
* if the current {@link #getSubject(ServletRequest, ServletResponse) subject}
* is not <code>null</code>, <code>false</code> otherwise.
*
* @return <code>true</code> if the request is a
* {@link #isLoginRequest(ServletRequest, ServletResponse) loginRequest} or
* if the current {@link #getSubject(ServletRequest, ServletResponse) subject}
* is not <code>null</code>, <code>false</code> otherwise.
*/
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (isLoginRequest(request, response)) {
return true;
} else {
Subject subject = getSubject(request, response);
// If principal is not null, then the user is known and should be allowed access.
return subject.getPrincipal() != null;
}
}
/**
* This default implementation simply calls
* {@link #saveRequestAndRedirectToLogin(ServletRequest, ServletResponse) saveRequestAndRedirectToLogin}
* and then immediately returns <code>false</code>, thereby preventing the chain from continuing so the redirect may
* execute.
*/
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
/**
* 如果是ajax请求则不进行跳转
*/
if (httpServletRequest.getHeader("x-requested-with") != null
&& httpServletRequest.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest")) {
httpServletResponse.setHeader("sessionstatus", "timeout");
return false;
} else {
/**
* 第一次点击页面
*/
String referer = httpServletRequest.getHeader("Referer");
if (referer == null) {
saveRequestAndRedirectToLogin(request, response);
return false;
} else {
/**
* 从别的页面跳转过来的
*/
if (ShiroKit.getSession().getAttribute("sessionFlag") == null) {
httpServletRequest.setAttribute("tips", "session超时");
httpServletRequest.getRequestDispatcher("/login").forward(request, response);
return false;
} else {
saveRequestAndRedirectToLogin(request, response);
return false;
}
}
}
}
}
/**
* Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.sys.core.shiro;
import cn.stylefeng.guns.base.shiro.ShiroUser;
import cn.stylefeng.guns.sys.core.shiro.service.UserAuthService;
import cn.stylefeng.guns.sys.core.shiro.service.impl.UserAuthServiceServiceImpl;
import cn.stylefeng.guns.sys.modular.system.entity.User;
import cn.stylefeng.roses.core.util.ToolUtil;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ShiroDbRealm extends AuthorizingRealm {
/**
* 登录认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
throws AuthenticationException {
UserAuthService shiroFactory = UserAuthServiceServiceImpl.me();
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
User user = shiroFactory.user(token.getUsername());
ShiroUser shiroUser = shiroFactory.shiroUser(user);
return shiroFactory.info(shiroUser, user, super.getName());
}
/**
* 权限认证
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
UserAuthService shiroFactory = UserAuthServiceServiceImpl.me();
ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
List<Long> roleList = shiroUser.getRoleList();
Set<String> permissionSet = new HashSet<>();
Set<String> roleNameSet = new HashSet<>();
for (Long roleId : roleList) {
List<String> permissions = shiroFactory.findPermissionsByRoleId(roleId);
if (permissions != null) {
for (String permission : permissions) {
if (ToolUtil.isNotEmpty(permission)) {
permissionSet.add(permission);
}
}
}
String roleName = shiroFactory.findRoleNameByRoleId(roleId);
roleNameSet.add(roleName);
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(permissionSet);
info.addRoles(roleNameSet);
return info;
}
}
package cn.stylefeng.guns.sys.core.shiro.oauth;
/**
* 登录类型
*
* @author fengshuonan
* @Date 2019/6/9 17:24
*/
public enum LoginType {
PASSWORD, OAUTH_TOKEN
}
package cn.stylefeng.guns.sys.core.shiro.oauth;
import lombok.Data;
import org.apache.shiro.authc.UsernamePasswordToken;
/**
* token登陆类型
*
* @author fengshuonan
* @Date 2019/6/9 17:25
*/
@Data
public class OAuthToken extends UsernamePasswordToken {
private LoginType type;
/**
* 免密登录
*/
public OAuthToken(String username) {
super(username, "", false, null);
this.type = LoginType.OAUTH_TOKEN;
}
/**
* 账号密码登录
*/
public OAuthToken(String username, char[] pwd) {
super(username, pwd, false, null);
this.type = LoginType.PASSWORD;
}
}
package cn.stylefeng.guns.sys.core.shiro.oauth.matcher;
import cn.stylefeng.guns.sys.core.shiro.oauth.LoginType;
import cn.stylefeng.guns.sys.core.shiro.oauth.OAuthToken;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
/**
* 针对旧的shiro密码校验的拓展(带oauth token的登录校验)
*
* @author fengshuonan
* @Date 2019/6/9 19:25
*/
public class WithOAuthTokenMatcher extends HashedCredentialsMatcher {
public WithOAuthTokenMatcher() {
}
public WithOAuthTokenMatcher(String hashAlgorithmName) {
super(hashAlgorithmName);
}
@Override
public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) {
if (authcToken instanceof OAuthToken) {
//如果是oauth token登录,直接跳过密码校验
OAuthToken oAuthToken = (OAuthToken) authcToken;
if (oAuthToken.getType().equals(LoginType.OAUTH_TOKEN)) {
return true;
} else {
return false;
}
} else {
//不是免密登录,调用父类的方法
return super.doCredentialsMatch(authcToken, info);
}
}
}
/**
* Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.sys.core.shiro.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.stylefeng.guns.base.shiro.ShiroUser;
import cn.stylefeng.guns.sys.core.listener.ConfigListener;
import cn.stylefeng.guns.sys.core.shiro.ShiroKit;
import cn.stylefeng.guns.sys.core.shiro.service.PermissionCheckService;
import cn.stylefeng.roses.core.util.HttpContext;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
/**
* 权限自定义检查
*/
@Service
@DependsOn("springContextHolder")
@Transactional(readOnly = true)
public class PermissionCheckServiceServiceImpl implements PermissionCheckService {
@Override
public boolean check(Object[] permissions) {
ShiroUser user = ShiroKit.getUser();
if (null == user) {
return false;
}
ArrayList<Object> objects = CollectionUtil.newArrayList(permissions);
String join = CollectionUtil.join(objects, ",");
if (ShiroKit.hasAnyRoles(join)) {
return true;
}
return false;
}
@Override
public boolean checkAll() {
HttpServletRequest request = HttpContext.getRequest();
ShiroUser user = ShiroKit.getUser();
if (null == user) {
return false;
}
String requestURI = request.getRequestURI().replaceFirst(ConfigListener.getConf().get("contextPath"), "");
String[] str = requestURI.split("/");
if (str.length > 3) {
requestURI = "/" + str[1] + "/" + str[2];
}
if (ShiroKit.hasPermission(requestURI)) {
return true;
}
return false;
}
}
/**
* Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.sys.core.shiro.service.impl;
import cn.hutool.core.convert.Convert;
import cn.stylefeng.guns.base.shiro.ShiroUser;
import cn.stylefeng.guns.sys.core.constant.factory.ConstantFactory;
import cn.stylefeng.guns.sys.core.constant.state.ManagerStatus;
import cn.stylefeng.guns.sys.core.shiro.ShiroKit;
import cn.stylefeng.guns.sys.core.shiro.service.UserAuthService;
import cn.stylefeng.guns.sys.modular.system.entity.User;
import cn.stylefeng.guns.sys.modular.system.mapper.MenuMapper;
import cn.stylefeng.guns.sys.modular.system.mapper.UserMapper;
import cn.stylefeng.guns.sys.modular.system.service.DictService;
import cn.stylefeng.roses.core.util.SpringContextHolder;
import org.apache.shiro.authc.CredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
@DependsOn("springContextHolder")
@Transactional(readOnly = true)
public class UserAuthServiceServiceImpl implements UserAuthService {
@Autowired
private UserMapper userMapper;
@Autowired
private MenuMapper menuMapper;
@Autowired
private DictService dictService;
public static UserAuthService me() {
return SpringContextHolder.getBean(UserAuthService.class);
}
@Override
public User user(String account) {
User user = userMapper.getByAccount(account);
// 账号不存在
if (null == user) {
throw new CredentialsException();
}
// 账号被冻结
if (!user.getStatus().equals(ManagerStatus.OK.getCode())) {
throw new LockedAccountException();
}
return user;
}
@Override
public ShiroUser shiroUser(User user) {
ShiroUser shiroUser = ShiroKit.createShiroUser(user);
//用户角色数组
Long[] roleArray = Convert.toLongArray(user.getRoleId());
//获取用户角色列表
List<Long> roleList = new ArrayList<>();
List<String> roleNameList = new ArrayList<>();
for (Long roleId : roleArray) {
roleList.add(roleId);
roleNameList.add(ConstantFactory.me().getSingleRoleName(roleId));
}
shiroUser.setRoleList(roleList);
shiroUser.setRoleNames(roleNameList);
//根据角色获取系统的类型
List<String> systemTypes = this.menuMapper.getMenusTypesByRoleIds(roleList);
//通过字典编码
List<Map<String, Object>> dictsByCodes = dictService.getDictsByCodes(systemTypes);
shiroUser.setSystemTypes(dictsByCodes);
return shiroUser;
}
@Override
public List<String> findPermissionsByRoleId(Long roleId) {
return menuMapper.getResUrlsByRoleId(roleId);
}
@Override
public String findRoleNameByRoleId(Long roleId) {
return ConstantFactory.me().getSingleRoleTip(roleId);
}
@Override
public SimpleAuthenticationInfo info(ShiroUser shiroUser, User user, String realmName) {
String credentials = user.getPassword();
// 密码加盐处理
String source = user.getSalt();
ByteSource credentialsSalt = new Md5Hash(source);
return new SimpleAuthenticationInfo(shiroUser, credentials, credentialsSalt, realmName);
}
}
/**
* Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.sys.core.util;
import cn.stylefeng.roses.core.util.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import java.util.List;
/**
* 缓存工具类
*/
@Slf4j
public class CacheUtil {
private static final Object LOCKER = new Object();
public static void put(String cacheName, Object key, Object value) {
getOrAddCache(cacheName).put(new Element(key, value));
}
@SuppressWarnings("all")
public static <T> T get(String cacheName, Object key) {
Element element = getOrAddCache(cacheName).get(key);
if (element == null) {
return null;
} else {
Object objectValue = element.getObjectValue();
return (T) objectValue;
}
}
public static List getKeys(String cacheName) {
return getOrAddCache(cacheName).getKeys();
}
public static void remove(String cacheName, Object key) {
getOrAddCache(cacheName).remove(key);
}
public static void removeAll(String cacheName) {
getOrAddCache(cacheName).removeAll();
}
private static Cache getOrAddCache(String cacheName) {
CacheManager cacheManager = SpringContextHolder.getBean(CacheManager.class);
Cache cache = cacheManager.getCache(cacheName);
if (cache == null) {
synchronized (LOCKER) {
cache = cacheManager.getCache(cacheName);
if (cache == null) {
cacheManager.addCacheIfAbsent(cacheName);
cache = cacheManager.getCache(cacheName);
}
}
}
return cache;
}
}
......@@ -15,8 +15,8 @@
*/
package cn.stylefeng.guns.sys.core.util;
import cn.stylefeng.guns.base.auth.context.LoginContextHolder;
import cn.stylefeng.guns.sys.core.listener.ConfigListener;
import cn.stylefeng.guns.sys.core.shiro.ShiroKit;
/**
* 获取默认图片地址
......@@ -43,7 +43,7 @@ public class DefaultImages {
* @Date 2018/10/30 5:51 PM
*/
public static String defaultAvatarUrl() {
if (ShiroKit.oauth2Flag()) {
if (LoginContextHolder.getContext().oauth2Flag()) {
return ConfigListener.getConf().get("contextPath") + "/oauth/avatar";
} else {
return ConfigListener.getConf().get("contextPath") + "/system/previewAvatar";
......
/**
* Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.sys.core.util;
import cn.stylefeng.guns.base.consts.ConstantsContext;
/**
* 验证码工具类
*/
public class KaptchaUtil {
/**
* 获取验证码开关
*/
public static Boolean getKaptchaOnOff() {
return ConstantsContext.getKaptchaOpen();
}
}
\ No newline at end of file
package cn.stylefeng.guns.sys.core.util;
import cn.stylefeng.roses.core.util.MD5Util;
import cn.stylefeng.roses.core.util.ToolUtil;
/**
* 密码加盐的工具
*
* @author fengshuonan
* @Date 2019/7/20 17:34
*/
public class SaltUtil {
/**
* 获取密码盐
*
* @author fengshuonan
* @Date 2019/7/20 17:35
*/
public static String getRandomSalt() {
return ToolUtil.getRandomString(5);
}
/**
* md5加密,带盐值
*
* @author fengshuonan
* @Date 2019/7/20 17:36
*/
public static String md5Encrypt(String password, String salt) {
if (ToolUtil.isOneEmpty(password, salt)) {
throw new IllegalArgumentException("密码或盐为空!");
} else {
return MD5Util.encrypt(password + salt);
}
}
}
package cn.stylefeng.guns.sys.core.util;
import cn.hutool.core.io.FileUtil;
import cn.stylefeng.guns.base.consts.ConstantsContext;
import cn.stylefeng.guns.sys.core.listener.ConfigListener;
import cn.stylefeng.guns.sys.modular.system.model.UeditorFileResult;
import cn.stylefeng.roses.core.util.ToolUtil;
import cn.stylefeng.roses.kernel.model.exception.ServiceException;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import static cn.stylefeng.guns.sys.core.exception.enums.BizExceptionEnum.*;
/**
* 百度富文本的工具类
*
* @author fengshuonan
* @Date 2019/8/26 15:27
*/
@Slf4j
public class UeditorUtil {
/**
* ue上传文件逻辑
*
* @author fengshuonan
* @Date 2019-08-27 12:54
*/
public static UeditorFileResult uploadFile(MultipartFile upfile, FileType fileType) {
if (upfile.isEmpty()) {
throw new ServiceException(UE_FILE_NULL_ERROR);
}
// 获取文件名,后缀名
String oldFileName = upfile.getOriginalFilename();
String suffixName = ToolUtil.getFileSuffix(oldFileName);
// 重新命名图片
String newFileName = IdWorker.getIdStr() + "." + suffixName;
UeditorFileResult ueditorFileResult = new UeditorFileResult();
String path = null;
// 如果是上传图片
if (fileType.equals(FileType.IMG)) {
ueditorFileResult.setUrl(UeditorUtil.getImageRelativeUrl(newFileName));
ueditorFileResult.setTitle(newFileName);
ueditorFileResult.setOriginal(newFileName);
//文件用原始文件
path = ConstantsContext.getFileUploadPath() + newFileName;
} else if (fileType.equals(FileType.FILE)) {
// 如果是上传文件
ueditorFileResult.setUrl(UeditorUtil.getFileRelativeUrl(newFileName) + "/" + oldFileName);
ueditorFileResult.setTitle(oldFileName);
ueditorFileResult.setOriginal(oldFileName);
//文件用原始文件
path = ConstantsContext.getFileUploadPath() + newFileName;
} else {
// 如果是上传视频
ueditorFileResult.setUrl(UeditorUtil.getVideoRelativeUrl(newFileName));
ueditorFileResult.setTitle(newFileName);
ueditorFileResult.setOriginal(newFileName);
//文件用原始文件
path = ConstantsContext.getFileUploadPath() + newFileName;
}
try {
File dest = new File(path);
if (!dest.getParentFile().exists()) {
dest.getParentFile().mkdirs();
}
upfile.transferTo(dest);
return ueditorFileResult;
} catch (IOException e) {
log.error("保存ue的上传文件出错!", e);
throw new ServiceException(UE_FILE_SAVE_ERROR);
}
}
/**
* 读取文件
*
* @author fengshuonan
* @Date 2019-08-27 13:02
*/
public static void readFile(String fileName, HttpServletResponse response, FileType fileType, String orginalName) {
if (ToolUtil.isEmpty(fileName)) {
throw new ServiceException(UE_FILE_NULL_ERROR);
}
//获取文件路径
String path = ConstantsContext.getFileUploadPath() + fileName;
File file = new File(path);
//文件不存在或者不可读
if (!file.exists() || !file.canRead()) {
throw new ServiceException(UE_FILE_NULL_ERROR);
}
//读取文件
byte[] bytes = null;
//设置响应的类型
if (fileType.equals(FileType.IMG)) {
response.setContentType("image/png");
bytes = FileUtil.readBytes(file);
} else if (fileType.equals(FileType.FILE)) {
response.setContentType("multipart/form-data;charset=utf-8");
//判断文件是否已经被重命名
String newFilePath = ConstantsContext.getFileUploadPath() + orginalName;
File newFile = new File(newFilePath);
if (!newFile.exists()) {
newFile = UeditorUtil.reName(file, orginalName);
}
bytes = FileUtil.readBytes(newFile);
} else {
response.setContentType("video/x-sgi-movie");
bytes = FileUtil.readBytes(file);
}
try {
OutputStream stream = response.getOutputStream();
stream.write(bytes);
} catch (IOException e) {
log.error("读取文件错误!", e);
throw new ServiceException(UE_FILE_READ_ERROR);
}
}
/**
* 获取图片相对路径
*
* @author fengshuonan
* @Date 2019/8/26 15:21
*/
private static String getImageRelativeUrl(String imageName) {
String contextPath = ConfigListener.getConf().get("contextPath");
return contextPath + "/ueditor/images/" + imageName;
}
/**
* 获取文件相对路径
*
* @author fengshuonan
* @Date 2019/8/26 15:22
*/
private static String getFileRelativeUrl(String imageName) {
String contextPath = ConfigListener.getConf().get("contextPath");
return contextPath + "/ueditor/file/" + imageName;
}
/**
* 获取视频相对路径
*
* @author fengshuonan
* @Date 2019/8/26 15:22
*/
private static String getVideoRelativeUrl(String imageName) {
String contextPath = ConfigListener.getConf().get("contextPath");
return contextPath + "/ueditor/video/" + imageName;
}
/**
* 文件重命名
*
* @author fengshuonan
* @Date 2019/8/26 15:23
*/
private static File reName(File file, String newName) {
if (file.exists()) {
//创建新名字的抽象文件
File newfile = new File(file.getParent() + File.separator + newName);
if (file.renameTo(newfile)) {
log.info("重命名成功!");
return newfile;
} else {
log.info("重命名失败!新文件名已存在!");
return file;
}
} else {
log.info("重命名文件不存在!");
return file;
}
}
/**
* 文件类型
*
* @author fengshuonan
* @Date 2019-08-27 12:52
*/
public enum FileType {
/**
* 图片类型
*/
IMG,
/**
* 文件类型
*/
FILE,
/**
* 视频类型
*/
VIDEO
}
}
package cn.stylefeng.guns.sys.modular.consts.service;
package cn.stylefeng.guns.sys.modular.consts.init;
import cn.stylefeng.guns.base.consts.ConstantsContext;
import cn.stylefeng.guns.sys.modular.consts.entity.SysConfig;
import cn.stylefeng.guns.sys.modular.consts.service.SysConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
......
......@@ -4,7 +4,6 @@ import cn.stylefeng.guns.sys.modular.consts.entity.SysConfig;
import cn.stylefeng.guns.sys.modular.consts.model.params.SysConfigParam;
import cn.stylefeng.guns.sys.modular.consts.model.result.SysConfigResult;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
......
......@@ -64,6 +64,6 @@ public interface SysConfigService extends IService<SysConfig> {
* @author stylefeng
* @Date 2019-06-20
*/
LayuiPageInfo findPageBySpec(SysConfigParam param);
LayuiPageInfo findPageBySpec(SysConfigParam param);
}
package cn.stylefeng.guns.sys.modular.db.controller;
import cn.stylefeng.guns.base.db.entity.DatabaseInfo;
import cn.stylefeng.guns.base.db.util.DbUtil;
import cn.stylefeng.guns.base.pojo.page.LayuiPageFactory;
import cn.stylefeng.guns.base.pojo.page.LayuiPageInfo;
import cn.stylefeng.guns.base.db.util.DbUtil;
import cn.stylefeng.guns.base.db.entity.DatabaseInfo;
import cn.stylefeng.guns.sys.modular.db.model.params.DatabaseInfoParam;
import cn.stylefeng.guns.sys.modular.db.service.DatabaseInfoService;
import cn.stylefeng.roses.core.base.controller.BaseController;
......@@ -65,17 +65,6 @@ public class DatabaseInfoController extends BaseController {
}
/**
* 编辑页面
*
* @author stylefeng
* @Date 2019-06-15
*/
@RequestMapping("/edit")
public String edit() {
return PREFIX + "/databaseInfo_edit.html";
}
/**
* 新增接口
*
* @author stylefeng
......@@ -115,19 +104,6 @@ public class DatabaseInfoController extends BaseController {
}
/**
* 查看详情接口
*
* @author stylefeng
* @Date 2019-06-15
*/
@RequestMapping("/detail")
@ResponseBody
public ResponseData detail(DatabaseInfoParam databaseInfoParam) {
DatabaseInfo detail = this.databaseInfoService.getById(databaseInfoParam.getDbId());
return ResponseData.success(detail);
}
/**
* 查询列表
*
* @author stylefeng
......@@ -135,7 +111,7 @@ public class DatabaseInfoController extends BaseController {
*/
@ResponseBody
@RequestMapping("/list")
public LayuiPageInfo list(@RequestParam(value = "condition",required = false) String condition) {
public LayuiPageInfo list(@RequestParam(value = "condition", required = false) String condition) {
DatabaseInfoParam databaseInfoParam = new DatabaseInfoParam();
databaseInfoParam.setDbName(condition);
return this.databaseInfoService.findPageBySpec(databaseInfoParam);
......
package cn.stylefeng.guns.sys.modular.db.factory;
import cn.stylefeng.guns.sys.modular.db.model.params.DatabaseInfoParam;
import cn.stylefeng.roses.core.config.properties.DruidProperties;
/**
* 数据库信息工厂
*
* @author fengshuonan
* @date 2019-06-15-20:05
*/
public class DataBaseInfoFactory {
/**
* 数据库信息工厂
*
* @author fengshuonan
* @Date 2019-06-15 20:05
*/
public static DatabaseInfoParam createDataBaseInfo(DruidProperties druidProperties, String databaseName) {
DatabaseInfoParam databaseInfo = new DatabaseInfoParam();
databaseInfo.setDbName(databaseName);
databaseInfo.setJdbcDriver(druidProperties.getDriverClassName());
databaseInfo.setUserName(druidProperties.getUsername());
databaseInfo.setPassword(druidProperties.getPassword());
//根据旧的url,拼接新的url
String jdbcUrl = druidProperties.getUrl();
int first = jdbcUrl.lastIndexOf("/") + 1;
int last = jdbcUrl.indexOf("?");
//替换旧的名称
String newUrl = jdbcUrl.substring(0, first) + databaseName + jdbcUrl.substring(last);
databaseInfo.setJdbcUrl(newUrl);
return databaseInfo;
}
}
......@@ -16,10 +16,9 @@
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
db_id AS "dbId", db_name AS "dbName", jdbc_driver AS "jdbcDriver", user_name AS "userName", password AS "password", jdbc_url AS "jdbcUrl", remarks AS "remarks", create_time AS "createTime"
db_id AS "dbId", db_name AS "dbName", jdbc_driver AS "jdbcDriver", user_name AS "userName", '***' AS "password", jdbc_url AS "jdbcUrl", remarks AS "remarks", create_time AS "createTime"
</sql>
<select id="customList" resultType="cn.stylefeng.guns.sys.modular.db.model.result.DatabaseInfoResult" parameterType="cn.stylefeng.guns.sys.modular.db.model.params.DatabaseInfoParam">
select
<include refid="Base_Column_List"/>
......
......@@ -64,6 +64,6 @@ public interface DatabaseInfoService extends IService<DatabaseInfo> {
* @author stylefeng
* @Date 2019-06-15
*/
LayuiPageInfo findPageBySpec(DatabaseInfoParam param);
LayuiPageInfo findPageBySpec(DatabaseInfoParam param);
}
package cn.stylefeng.guns.sys.modular.db.service.impl;
import cn.stylefeng.guns.base.pojo.page.LayuiPageFactory;
import cn.stylefeng.guns.base.pojo.page.LayuiPageInfo;
import cn.stylefeng.guns.base.db.context.SqlSessionFactoryContext;
import cn.stylefeng.guns.base.db.exception.DataSourceInitException;
import cn.stylefeng.guns.base.db.entity.DatabaseInfo;
import cn.stylefeng.guns.base.db.exception.DataSourceInitException;
import cn.stylefeng.guns.base.pojo.page.LayuiPageFactory;
import cn.stylefeng.guns.base.pojo.page.LayuiPageInfo;
import cn.stylefeng.guns.sys.modular.db.mapper.DatabaseInfoMapper;
import cn.stylefeng.guns.sys.modular.db.model.params.DatabaseInfoParam;
import cn.stylefeng.guns.sys.modular.db.model.result.DatabaseInfoResult;
......
......@@ -6,6 +6,7 @@ import cn.stylefeng.guns.base.db.entity.DatabaseInfo;
import cn.stylefeng.guns.base.pojo.page.LayuiPageInfo;
import cn.stylefeng.guns.generator.generator.base.model.ContextParam;
import cn.stylefeng.guns.generator.generator.guns.GunsExecutor;
import cn.stylefeng.guns.generator.generator.restful.RestfulApiExecutor;
import cn.stylefeng.guns.generator.generator.restful.mybatisplus.param.MpParam;
import cn.stylefeng.guns.generator.util.ConcatUtil;
import cn.stylefeng.guns.generator.util.MapperConditionMapHolder;
......@@ -152,7 +153,8 @@ public class GeneratorController {
@RequestMapping(value = "/execute")
@ResponseBody
public ResponseEntity<InputStreamResource> execute(String author, String proPackage, String removePrefix,
Long dataSourceId, String tables, String modularName) {
Long dataSourceId, String tables, String modularName,
String version, String swagger, String remote) {
//获取字符串拼接数组
String[] tableArray = ConcatUtil.getArray(tables);
......@@ -166,6 +168,16 @@ public class GeneratorController {
contextParam.setJdbcUserName(databaseInfo.getUserName());
contextParam.setJdbcPassword(databaseInfo.getPassword());
contextParam.setJdbcUrl(databaseInfo.getJdbcUrl());
if ("Y".equalsIgnoreCase(swagger)) {
contextParam.setSwagger(true);
} else {
contextParam.setSwagger(false);
}
if ("Y".equalsIgnoreCase(remote)) {
contextParam.setRemote(true);
} else {
contextParam.setRemote(false);
}
//处理modularName,如果modularName传值不为空,则待上左斜杠路径
if (ToolUtil.isNotEmpty(modularName)) {
......@@ -190,9 +202,18 @@ public class GeneratorController {
Map<String, String[]> attribute = (Map<String, String[]>) HttpContext.getRequest().getSession().getAttribute(CONDITION_FIELDS);
MapperConditionMapHolder.put(attribute);
try {
//代码生成contextParam
GunsExecutor.executor(contextParam, mpContextParam);
//如果是Guns单体版本生成
if (version.equalsIgnoreCase("single")) {
GunsExecutor.executor(contextParam, mpContextParam);
} else {
//如果是微服务版本代码生成
RestfulApiExecutor.executor(contextParam, mpContextParam);
}
} finally {
MapperConditionMapHolder.remove();
}
......
/**
* Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.sys.modular.rest;
import cn.stylefeng.guns.api.core.util.JwtTokenUtil;
import cn.stylefeng.guns.base.shiro.ShiroUser;
import cn.stylefeng.guns.sys.core.shiro.ShiroKit;
import cn.stylefeng.guns.sys.modular.system.entity.User;
import cn.stylefeng.guns.sys.modular.system.mapper.UserMapper;
import cn.stylefeng.roses.core.base.controller.BaseController;
import cn.stylefeng.roses.core.reqres.response.ErrorResponseData;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
/**
* api登录接口,获取token
*
* @author stylefeng
* @Date 2018/7/20 23:39
*/
@RestController
@RequestMapping("/gunsApi")
public class ApiLoginController extends BaseController {
@Autowired
private UserMapper userMapper;
/**
* api登录接口,通过账号密码获取token
*/
@RequestMapping("/auth")
public Object auth(@RequestParam("username") String username,
@RequestParam("password") String password) {
//封装请求账号密码为shiro可验证的token
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password.toCharArray());
//获取数据库中的账号密码,准备比对
User user = userMapper.getByAccount(username);
String credentials = user.getPassword();
String salt = user.getSalt();
ByteSource credentialsSalt = new Md5Hash(salt);
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
new ShiroUser(), credentials, credentialsSalt, "");
//校验用户账号密码
HashedCredentialsMatcher md5CredentialsMatcher = new HashedCredentialsMatcher();
md5CredentialsMatcher.setHashAlgorithmName(ShiroKit.hashAlgorithmName);
md5CredentialsMatcher.setHashIterations(ShiroKit.hashIterations);
boolean passwordTrueFlag = md5CredentialsMatcher.doCredentialsMatch(
usernamePasswordToken, simpleAuthenticationInfo);
if (passwordTrueFlag) {
HashMap<String, Object> result = new HashMap<>();
result.put("token", JwtTokenUtil.generateToken(String.valueOf(user.getUserId())));
return result;
} else {
return new ErrorResponseData(500, "账号密码错误!");
}
}
}
/**
* Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.sys.modular.rest.controller;
import cn.hutool.core.bean.BeanUtil;
import cn.stylefeng.guns.base.log.BussinessLog;
import cn.stylefeng.guns.base.pojo.node.ZTreeNode;
import cn.stylefeng.guns.sys.core.constant.dictmap.DeptDict;
import cn.stylefeng.guns.sys.core.constant.factory.ConstantFactory;
import cn.stylefeng.guns.sys.modular.rest.entity.RestDept;
import cn.stylefeng.guns.sys.modular.rest.factory.DeptFactory;
import cn.stylefeng.guns.sys.modular.rest.model.DeptTreeNode;
import cn.stylefeng.guns.sys.modular.rest.service.RestDeptService;
import cn.stylefeng.guns.sys.modular.system.model.DeptDto;
import cn.stylefeng.guns.sys.modular.system.warpper.DeptWrapper;
import cn.stylefeng.roses.core.base.controller.BaseController;
import cn.stylefeng.roses.core.reqres.response.ResponseData;
import cn.stylefeng.roses.core.reqres.response.SuccessResponseData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
/**
* 部门控制器
*
* @author fengshuonan
* @Date 2017年2月17日20:27:22
*/
@RestController
@RequestMapping("/rest/dept")
public class RestDeptController extends BaseController {
@Autowired
private RestDeptService restDeptService;
/**
* 获取部门的tree列表,ztree格式
*
* @author fengshuonan
* @Date 2018/12/23 4:56 PM
*/
@RequestMapping(value = "/tree")
public List<ZTreeNode> tree() {
List<ZTreeNode> tree = this.restDeptService.tree();
tree.add(ZTreeNode.createParent());
return tree;
}
/**
* 新增部门
*
* @author fengshuonan
* @Date 2018/12/23 4:57 PM
*/
@BussinessLog(value = "添加部门", key = "simpleName", dict = DeptDict.class)
@RequestMapping(value = "/add")
public ResponseData add(RestDept restDept) {
this.restDeptService.addDept(restDept);
return SUCCESS_TIP;
}
/**
* 获取所有部门列表
*
* @author fengshuonan
* @Date 2018/12/23 4:57 PM
*/
@RequestMapping(value = "/list")
public Object list(@RequestParam(value = "condition", required = false) String condition,
@RequestParam(value = "deptId", required = false) Long deptId) {
List<Map<String, Object>> list = this.restDeptService.list(condition, deptId);
List<Map<String, Object>> wrap = new DeptWrapper(list).wrap();
//创建部门树
List<DeptTreeNode> deptTreeNodes = DeptFactory.buildTreeNodes(wrap);
return new SuccessResponseData(deptTreeNodes);
}
/**
* 部门详情
*
* @author fengshuonan
* @Date 2018/12/23 4:57 PM
*/
@RequestMapping(value = "/detail/{deptId}")
public Object detail(@PathVariable("deptId") Long deptId) {
RestDept dept = restDeptService.getById(deptId);
DeptDto deptDto = new DeptDto();
BeanUtil.copyProperties(dept, deptDto);
deptDto.setPName(ConstantFactory.me().getDeptName(deptDto.getPid()));
return deptDto;
}
/**
* 修改部门
*
* @author fengshuonan
* @Date 2018/12/23 4:57 PM
*/
@BussinessLog(value = "修改部门", key = "simpleName", dict = DeptDict.class)
@RequestMapping(value = "/update")
public ResponseData update(RestDept restDept) {
restDeptService.editDept(restDept);
return SUCCESS_TIP;
}
/**
* 删除部门
*
* @author fengshuonan
* @Date 2018/12/23 4:57 PM
*/
@BussinessLog(value = "删除部门", key = "deptId", dict = DeptDict.class)
@RequestMapping(value = "/delete")
public ResponseData delete(@RequestParam Long deptId) {
restDeptService.deleteDept(deptId);
return SUCCESS_TIP;
}
}
package cn.stylefeng.guns.sys.modular.rest.controller;
import cn.stylefeng.guns.base.pojo.node.ZTreeNode;
import cn.stylefeng.guns.base.pojo.page.LayuiPageInfo;
import cn.stylefeng.guns.sys.modular.rest.entity.RestDict;
import cn.stylefeng.guns.sys.modular.rest.service.RestDictService;
import cn.stylefeng.guns.sys.modular.system.model.params.DictParam;
import cn.stylefeng.guns.sys.modular.system.model.result.DictResult;
import cn.stylefeng.roses.core.base.controller.BaseController;
import cn.stylefeng.roses.core.reqres.response.ResponseData;
import cn.stylefeng.roses.core.reqres.response.SuccessResponseData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 基础字典控制器
*
* @author stylefeng
* @Date 2019-03-13 13:53:53
*/
@RestController
@RequestMapping("/rest/dict")
public class RestDictController extends BaseController {
@Autowired
private RestDictService restDictService;
/**
* 新增接口
*
* @author stylefeng
* @Date 2019-03-13
*/
@RequestMapping("/addItem")
public ResponseData addItem(DictParam dictParam) {
this.restDictService.add(dictParam);
return ResponseData.success();
}
/**
* 编辑接口
*
* @author stylefeng
* @Date 2019-03-13
*/
@RequestMapping("/editItem")
public ResponseData editItem(DictParam dictParam) {
this.restDictService.update(dictParam);
return ResponseData.success();
}
/**
* 删除接口
*
* @author stylefeng
* @Date 2019-03-13
*/
@RequestMapping("/delete")
public ResponseData delete(DictParam dictParam) {
this.restDictService.delete(dictParam);
return ResponseData.success();
}
/**
* 查看详情接口
*
* @author stylefeng
* @Date 2019-03-13
*/
@RequestMapping("/detail")
public ResponseData detail(DictParam dictParam) {
DictResult dictResult = this.restDictService.dictDetail(dictParam.getDictId());
return ResponseData.success(dictResult);
}
/**
* 查询列表
*
* @author stylefeng
* @Date 2019-03-13
*/
@RequestMapping("/list")
public LayuiPageInfo list(DictParam dictParam) {
return this.restDictService.findPageBySpec(dictParam);
}
/**
* 获取某个字典类型下的所有字典
*
* @author stylefeng
* @Date 2019-03-13
*/
@RequestMapping("/listDicts")
public ResponseData listDicts(@RequestParam("dictTypeId") Long dictTypeId) {
List<RestDict> dicts = this.restDictService.listDicts(dictTypeId);
return new SuccessResponseData(dicts);
}
/**
* 获取某个字典类型下的所有字典
*
* @author stylefeng
* @Date 2019-03-13
*/
@RequestMapping("/listDictsByCode")
public ResponseData listDictsByCode(@RequestParam("dictTypeCode") String dictTypeCode) {
List<RestDict> dicts = this.restDictService.listDictsByCode(dictTypeCode);
return new SuccessResponseData(dicts);
}
/**
* 获取某个类型下字典树的列表,ztree格式
*
* @author fengshuonan
* @Date 2018/12/23 4:56 PM
*/
@RequestMapping(value = "/ztree")
public List<ZTreeNode> ztree(@RequestParam("dictTypeId") Long dictTypeId, @RequestParam(value = "dictId", required = false) Long dictId) {
return this.restDictService.dictTreeList(dictTypeId, dictId);
}
}
package cn.stylefeng.guns.sys.modular.rest.controller;
import cn.stylefeng.guns.base.pojo.page.LayuiPageInfo;
import cn.stylefeng.guns.sys.modular.rest.entity.RestDictType;
import cn.stylefeng.guns.sys.modular.rest.service.RestDictTypeService;
import cn.stylefeng.guns.sys.modular.system.model.params.DictTypeParam;
import cn.stylefeng.roses.core.base.controller.BaseController;
import cn.stylefeng.roses.core.reqres.response.ResponseData;
import cn.stylefeng.roses.core.reqres.response.SuccessResponseData;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 字典类型表控制器
*
* @author stylefeng
* @Date 2019-03-13 1:54
*/
@RestController
@RequestMapping("/rest/dictType")
public class RestDictTypeController extends BaseController {
@Autowired
private RestDictTypeService restDictTypeService;
/**
* 新增接口
*
* @author stylefeng
* @Date 2019-03-13
*/
@RequestMapping("/addItem")
public ResponseData addItem(DictTypeParam dictTypeParam) {
this.restDictTypeService.add(dictTypeParam);
return ResponseData.success();
}
/**
* 编辑接口
*
* @author stylefeng
* @Date 2019-03-13
*/
@RequestMapping("/editItem")
public ResponseData editItem(DictTypeParam dictTypeParam) {
this.restDictTypeService.update(dictTypeParam);
return ResponseData.success();
}
/**
* 删除接口
*
* @author stylefeng
* @Date 2019-03-13
*/
@RequestMapping("/delete")
public ResponseData delete(DictTypeParam dictTypeParam) {
this.restDictTypeService.delete(dictTypeParam);
return ResponseData.success();
}
/**
* 查看详情接口
*
* @author stylefeng
* @Date 2019-03-13
*/
@RequestMapping("/detail")
public ResponseData detail(DictTypeParam dictTypeParam) {
RestDictType detail = this.restDictTypeService.getById(dictTypeParam.getDictTypeId());
return ResponseData.success(detail);
}
/**
* 查询列表
*
* @author stylefeng
* @Date 2019-03-13
*/
@RequestMapping("/list")
public LayuiPageInfo list(DictTypeParam dictTypeParam) {
return this.restDictTypeService.findPageBySpec(dictTypeParam);
}
/**
* 查询所有字典
*
* @author stylefeng
* @Date 2019-03-13
*/
@RequestMapping("/listTypes")
public ResponseData listTypes() {
QueryWrapper<RestDictType> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.select("dict_type_id", "code", "name");
List<RestDictType> list = this.restDictTypeService.list(objectQueryWrapper);
return new SuccessResponseData(list);
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment