diff --git a/backend/src/main/java/io/dataease/auth/api/AuthApi.java b/backend/src/main/java/io/dataease/auth/api/AuthApi.java new file mode 100644 index 0000000000..b4bced4779 --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/api/AuthApi.java @@ -0,0 +1,29 @@ +package io.dataease.auth.api; + +import io.dataease.auth.api.dto.CurrentUserDto; +import io.dataease.auth.api.dto.LoginDto; +import io.swagger.annotations.Api; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + + +@Api(tags = "权限:权限管理") +@RequestMapping("/api/auth") +public interface AuthApi { + + + @PostMapping("/login") + void login(LoginDto loginDto); + + + @PostMapping("/userInfo") + CurrentUserDto userInfo(); + + @GetMapping("/isLogin") + Boolean isLogin(); + + + @GetMapping("/test") + String test(); +} diff --git a/backend/src/main/java/io/dataease/auth/api/dto/LoginDto.java b/backend/src/main/java/io/dataease/auth/api/dto/LoginDto.java new file mode 100644 index 0000000000..4b17ab4962 --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/api/dto/LoginDto.java @@ -0,0 +1,13 @@ +package io.dataease.auth.api.dto; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class LoginDto implements Serializable { + + private String username; + + private String password; +} diff --git a/backend/src/main/java/io/dataease/auth/config/F2CRealm.java b/backend/src/main/java/io/dataease/auth/config/F2CRealm.java new file mode 100644 index 0000000000..9c9fed8526 --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/config/F2CRealm.java @@ -0,0 +1,74 @@ +package io.dataease.auth.config; + +import io.dataease.auth.entity.JWTToken; +import io.dataease.auth.entity.SysUserEntity; +import io.dataease.auth.service.AuthUserService; +import io.dataease.auth.util.JWTUtils; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.SimpleAuthenticationInfo; +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 org.springframework.stereotype.Component; +import javax.annotation.Resource; +import java.util.Set; +import java.util.stream.Collectors; + + + +@Component +public class F2CRealm extends AuthorizingRealm { + + @Resource + private AuthUserService authUserService; + + + @Override + public boolean supports(AuthenticationToken token) { + return token instanceof JWTToken; + } + + //验证资源权限 + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { + String username = JWTUtils.getUsername(principals.toString()); + SysUserEntity user = authUserService.getUser(username); + Long userId = user.getUserId(); + SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); + Set role = authUserService.roles(userId).stream().collect(Collectors.toSet()); + simpleAuthorizationInfo.addRoles(role); + Set permission = authUserService.permissions(userId).stream().collect(Collectors.toSet()); + simpleAuthorizationInfo.addStringPermissions(permission); + return simpleAuthorizationInfo; + } + + //验证登录权限 + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException { + String token = (String) auth.getCredentials(); + // 解密获得username,用于和数据库进行对比 + String username = JWTUtils.getUsername(token); + if (username == null) { + throw new AuthenticationException("token invalid"); + } + + SysUserEntity user = authUserService.getUser(username); + if (user == null) { + throw new AuthenticationException("User didn't existed!"); + } + String pass = null; + try { + /*pass = RsaUtil.decryptByPrivateKey(RsaProperties.privateKey, userBean.getPassword());*/ + pass = user.getPassword(); + } catch (Exception e) { + e.printStackTrace(); + } + if (! JWTUtils.verify(token, username, pass)) { + throw new AuthenticationException("Username or password error"); + } + return new SimpleAuthenticationInfo(token, token, "f2cReam"); + } +} diff --git a/backend/src/main/java/io/dataease/auth/config/ShiroConfig.java b/backend/src/main/java/io/dataease/auth/config/ShiroConfig.java new file mode 100644 index 0000000000..07f6e16db5 --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/config/ShiroConfig.java @@ -0,0 +1,82 @@ +package io.dataease.auth.config; + + +import io.dataease.auth.service.ShiroService; +import org.apache.shiro.mgt.DefaultSessionStorageEvaluator; +import org.apache.shiro.mgt.DefaultSubjectDAO; +import org.apache.shiro.spring.LifecycleBeanPostProcessor; +import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; +import org.apache.shiro.web.mgt.DefaultWebSecurityManager; +import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import javax.servlet.Filter; +import java.util.LinkedHashMap; +import java.util.Map; +import io.dataease.auth.filter.*; +import org.springframework.context.annotation.DependsOn; + +@Configuration +public class ShiroConfig { + + + + @Bean("securityManager") + public DefaultWebSecurityManager getManager(F2CRealm f2cRealm) { + DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); + // 使用自己的realm + manager.setRealm(f2cRealm); + + /* + * 关闭shiro自带的session,详情见文档 + * http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29 + */ + DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); + DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator(); + defaultSessionStorageEvaluator.setSessionStorageEnabled(false); + subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator); + manager.setSubjectDAO(subjectDAO); + + return manager; + } + + @Bean("shiroFilter") + public ShiroFilterFactoryBean factory(DefaultWebSecurityManager securityManager, ShiroService shiroService) { + ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); + // 添加自己的过滤器并且取名为jwt + Map filterMap = new LinkedHashMap<>(); + filterMap.put("f2cPerms", new F2CPermissionsFilter()); + //filterMap.put("f2cRoles", new F2CRolesFilter()); + filterMap.put("jwt", new JWTFilter()); + filterMap.put("logout", new F2CLogoutFilter()); + factoryBean.setFilters(filterMap); + factoryBean.setSecurityManager(securityManager); + factoryBean.setUnauthorizedUrl("/permissionMiss"); + factoryBean.setFilterChainDefinitionMap(shiroService.loadFilterChainDefinitionMap()); + return factoryBean; + } + + /** + * 下面的代码是添加注解支持 + */ + @Bean + @DependsOn("lifecycleBeanPostProcessor") + public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { + DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); + defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); + return defaultAdvisorAutoProxyCreator; + } + + @Bean + public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { + return new LifecycleBeanPostProcessor(); + } + + @Bean + public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { + AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); + advisor.setSecurityManager(securityManager); + return advisor; + } +} diff --git a/backend/src/main/java/io/dataease/auth/entity/JWTToken.java b/backend/src/main/java/io/dataease/auth/entity/JWTToken.java new file mode 100644 index 0000000000..afd4f3f5a9 --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/entity/JWTToken.java @@ -0,0 +1,22 @@ +package io.dataease.auth.entity; + +import org.apache.shiro.authc.AuthenticationToken; + +public class JWTToken implements AuthenticationToken { + + private String token; + + public JWTToken(String token) { + this.token = token; + } + + @Override + public Object getPrincipal() { + return token; + } + + @Override + public Object getCredentials() { + return token; + } +} diff --git a/backend/src/main/java/io/dataease/auth/entity/SysUserEntity.java b/backend/src/main/java/io/dataease/auth/entity/SysUserEntity.java new file mode 100644 index 0000000000..079d42fd5b --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/entity/SysUserEntity.java @@ -0,0 +1,28 @@ +package io.dataease.auth.entity; + + +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import java.io.Serializable; + +@Data + +public class SysUserEntity implements Serializable { + + private Long userId; + + private String username; + + private String nickName; + + private Long deptId; + + private String password; + + private Integer enabled; + + private String email; + + private String phone; +} diff --git a/backend/src/main/java/io/dataease/auth/filter/F2CLogoutFilter.java b/backend/src/main/java/io/dataease/auth/filter/F2CLogoutFilter.java new file mode 100644 index 0000000000..cd5b76320f --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/filter/F2CLogoutFilter.java @@ -0,0 +1,26 @@ +package io.dataease.auth.filter; + +import org.apache.shiro.subject.Subject; +import org.apache.shiro.web.filter.authc.LogoutFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +public class F2CLogoutFilter extends LogoutFilter { + + private static final Logger logger = LoggerFactory.getLogger(F2CLogoutFilter.class); + + + @Override + protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { + Subject subject = getSubject(request, response); + try { + subject.logout(); + } catch (Exception ex) { + logger.error("退出登录错误",ex); + } + return true; + } +} diff --git a/backend/src/main/java/io/dataease/auth/filter/F2CPermissionsFilter.java b/backend/src/main/java/io/dataease/auth/filter/F2CPermissionsFilter.java new file mode 100644 index 0000000000..c3f6b4682f --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/filter/F2CPermissionsFilter.java @@ -0,0 +1,28 @@ +package io.dataease.auth.filter; + +import org.apache.shiro.subject.Subject; +import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.IOException; + +public class F2CPermissionsFilter extends PermissionsAuthorizationFilter { + + @Override + public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException { + Subject subject = getSubject(request, response); + String[] perms = (String[]) mappedValue; + if (perms != null && perms.length > 0) { + for (String str : perms) { + // 判断访问的用户是否拥有mappedValue权限 + if (subject.isPermitted(str)) { + return true; + } + } + return false; + } + return true; + } + +} diff --git a/backend/src/main/java/io/dataease/auth/filter/JWTFilter.java b/backend/src/main/java/io/dataease/auth/filter/JWTFilter.java new file mode 100644 index 0000000000..a89e7673fa --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/filter/JWTFilter.java @@ -0,0 +1,121 @@ +package io.dataease.auth.filter; + +import io.dataease.auth.entity.JWTToken; +import io.dataease.auth.util.JWTUtils; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.RequestMethod; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class JWTFilter extends BasicHttpAuthenticationFilter { + + + private Logger LOGGER = LoggerFactory.getLogger(this.getClass()); + + + /** + * 判断用户是否想要登入。 + * 检测header里面是否包含Authorization字段即可 + */ + @Override + protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) { + HttpServletRequest req = (HttpServletRequest) request; + String authorization = req.getHeader("Authorization"); + return authorization != null; + } + + /** + * + */ + @Override + protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception { + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + String authorization = httpServletRequest.getHeader("Authorization"); + + if (JWTUtils.needRefresh(authorization)){ + authorization = refreshToken(request, response); + } + JWTToken token = new JWTToken(authorization); + Subject subject = getSubject(request, response); + // 提交给realm进行登入,如果错误他会抛出异常并被捕获 + subject.login(token); + return true; + } + + + + /** + */ + @Override + protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { + if (isLoginAttempt(request, response)) { + try { + boolean loginSuccess = executeLogin(request, response); + return loginSuccess; + } catch (Exception e) { + response401(request, response); + } + } + return false; + } + + private String refreshToken(ServletRequest request, ServletResponse response) { + // 获取AccessToken(Shiro中getAuthzHeader方法已经实现) + String token = this.getAuthzHeader(request); + // 获取当前Token的帐号信息 + String username = JWTUtils.getUsername(token); + String password = "123456"; + try { + String newToken = JWTUtils.sign(username, password); + JWTToken jwtToken = new JWTToken(newToken); + this.getSubject(request, response).login(jwtToken); + // 设置响应的Header头新Token + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + httpServletResponse.addHeader("Access-Control-Expose-Headers", "Authorization"); + httpServletResponse.setHeader("Authorization", newToken); + return newToken; + }catch (Exception e){ + e.printStackTrace(); + } + return null; + } + + + /** + * 对跨域提供支持 + */ + @Override + protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin")); + httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE"); + httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers")); + // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态 + if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) { + httpServletResponse.setStatus(HttpStatus.OK.value()); + return false; + } + return super.preHandle(request, response); + } + + /** + * 将非法请求跳转到 /401 + */ + private void response401(ServletRequest req, ServletResponse resp) { + try { + HttpServletResponse httpServletResponse = (HttpServletResponse) resp; + httpServletResponse.sendRedirect("/401"); + } catch (IOException e) { + LOGGER.error(e.getMessage()); + } + } +} diff --git a/backend/src/main/java/io/dataease/auth/server/AuthServer.java b/backend/src/main/java/io/dataease/auth/server/AuthServer.java new file mode 100644 index 0000000000..86359957f6 --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/server/AuthServer.java @@ -0,0 +1,78 @@ +package io.dataease.auth.server; + +import io.dataease.auth.api.AuthApi; +import io.dataease.auth.api.dto.CurrentRoleDto; +import io.dataease.auth.api.dto.CurrentUserDto; +import io.dataease.auth.api.dto.LoginDto; +import io.dataease.auth.entity.SysUserEntity; +import io.dataease.auth.service.AuthUserService; +import io.dataease.auth.util.JWTUtils; +import io.dataease.commons.utils.BeanUtils; +import io.dataease.commons.utils.ServletUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +public class AuthServer implements AuthApi { + + @Autowired + private AuthUserService authUserService; + + + @Override + public void login(@RequestBody LoginDto loginDto) { + String username = loginDto.getUsername(); + String password = loginDto.getPassword(); + SysUserEntity user = authUserService.getUser(username); + String realPwd = user.getPassword(); + if (StringUtils.isEmpty(realPwd)){ + throw new RuntimeException("没有该用户!"); + } + /*String pwd = RsaUtil.decryptByPrivateKey(RsaProperties.privateKey, password); + String realPass = RsaUtil.decryptByPrivateKey(RsaProperties.privateKey, realPwd); + if (!StrUtil.equals(pwd, realPass)){ + throw new RuntimeException("密码错误!"); + }*/ + if (!StringUtils.equals(realPwd, password)){ + throw new RuntimeException("密码错误!"); + } + /*Map result = new HashMap<>(); + result.put("token", JWTUtils.sign(username, realPwd));*/ + String token = JWTUtils.sign(username, realPwd); + ServletUtils.setToken(token); + } + + @Override + public CurrentUserDto userInfo() { + String token = ServletUtils.getToken(); + String username = JWTUtils.getUsername(token); + SysUserEntity user = authUserService.getUser(username); + CurrentUserDto currentUserDto = BeanUtils.copyBean(new CurrentUserDto(), user); + List currentRoleDtos = authUserService.roleInfos(user.getUserId()); + currentUserDto.setRoles(currentRoleDtos); + return currentUserDto; + } + + @PostMapping("/logout") + public String logout(){ + return "success"; + } + + @Override + public Boolean isLogin() { + return null; + } + + @Override + public String test() { + return "apple"; + } +} diff --git a/backend/src/main/java/io/dataease/auth/service/AuthUserService.java b/backend/src/main/java/io/dataease/auth/service/AuthUserService.java new file mode 100644 index 0000000000..fe04b1aca2 --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/service/AuthUserService.java @@ -0,0 +1,22 @@ +package io.dataease.auth.service; + +import io.dataease.auth.api.dto.CurrentRoleDto; +import io.dataease.auth.entity.SysUserEntity; + +import java.util.List; + +public interface AuthUserService { + + + + SysUserEntity getUser(String username); + + List roles(Long userId); + + List permissions(Long userId); + + List roleInfos(Long userId); + + + +} diff --git a/backend/src/main/java/io/dataease/auth/service/ShiroService.java b/backend/src/main/java/io/dataease/auth/service/ShiroService.java new file mode 100644 index 0000000000..795521de3e --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/service/ShiroService.java @@ -0,0 +1,35 @@ +package io.dataease.auth.service; + +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; + +import java.util.Map; + +public interface ShiroService { + + /** + * 初始化权限 -> 拿全部权限 + * + * @param : + * @return: java.util.Map + */ + Map loadFilterChainDefinitionMap(); + + /** + * 在对uri权限进行增删改操作时,需要调用此方法进行动态刷新加载数据库中的uri权限 + * + * @param shiroFilterFactoryBean + * @param roleId + * @param isRemoveSession: + * @return: void + */ + void updatePermission(ShiroFilterFactoryBean shiroFilterFactoryBean, Integer roleId, Boolean isRemoveSession); + + /** + * shiro动态权限加载 -> 原理:删除shiro缓存,重新执行doGetAuthorizationInfo方法授权角色和权限 + * + * @param roleId + * @param isRemoveSession: + * @return: void + */ + void updatePermissionByRoleId(Integer roleId, Boolean isRemoveSession); +} diff --git a/backend/src/main/java/io/dataease/auth/service/impl/AuthUserServiceImpl.java b/backend/src/main/java/io/dataease/auth/service/impl/AuthUserServiceImpl.java new file mode 100644 index 0000000000..4a77044bda --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/service/impl/AuthUserServiceImpl.java @@ -0,0 +1,37 @@ +package io.dataease.auth.service.impl; + +import io.dataease.auth.api.dto.CurrentRoleDto; +import io.dataease.auth.entity.SysUserEntity; +import io.dataease.base.mapper.ext.AuthMapper; +import io.dataease.auth.service.AuthUserService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; + +@Service +public class AuthUserServiceImpl implements AuthUserService { + + @Resource + private AuthMapper authMapper; + + + @Override + public SysUserEntity getUser(String username){ + return authMapper.findUser(username); + } + @Override + public List roles(Long userId){ + return authMapper.roleCodes(userId); + } + @Override + public List permissions(Long userId){ + return authMapper.permissions(userId); + } + + @Override + public List roleInfos(Long userId) { + return authMapper.roles(userId); + } +} diff --git a/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java b/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java new file mode 100644 index 0000000000..55bd573940 --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java @@ -0,0 +1,67 @@ +package io.dataease.auth.service.impl; + +import io.dataease.auth.service.ShiroService; +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.StringJoiner; + +@Service +public class ShiroServiceImpl implements ShiroService { + + @Override + public Map loadFilterChainDefinitionMap() { + // 权限控制map + Map filterChainDefinitionMap = new LinkedHashMap<>(); + // 配置过滤:不会被拦截的链接 -> 放行 start ---------------------------------------------------------- + // 放行Swagger2页面,需要放行这些 + + filterChainDefinitionMap.put("/swagger-ui.html","anon"); + filterChainDefinitionMap.put("/swagger/**","anon"); + filterChainDefinitionMap.put("/webjars/**", "anon"); + filterChainDefinitionMap.put("/swagger-resources/**","anon"); + filterChainDefinitionMap.put("/v2/**","anon"); + filterChainDefinitionMap.put("/static/**", "anon"); + + // 登陆 + filterChainDefinitionMap.put("/api/auth/**", "anon"); + // 退出 + //filterChainDefinitionMap.put("/logout", "anon"); + // 放行未授权接口,重定向使用 + filterChainDefinitionMap.put("/unauth", "anon"); + filterChainDefinitionMap.put("/display/**", "anon"); + + // token过期接口 + filterChainDefinitionMap.put("/tokenExpired", "anon"); + // 被挤下线 + filterChainDefinitionMap.put("/downline", "anon"); + // 放行 end ---------------------------------------------------------- + filterChainDefinitionMap.put("/logout", "logout"); + + /*List extPermissionBeans = extUserMapper.getPermissions(); + + extPermissionBeans.forEach(item -> { + StringJoiner f2cPerms = new StringJoiner(",", "f2cPerms[", "]"); + f2cPerms.add(item.getPermission()); + filterChainDefinitionMap.put(item.getPath(), "jwt," + f2cPerms); + }); +*/ + + filterChainDefinitionMap.put("/**", "jwt"); + return filterChainDefinitionMap; + } + + @Override + public void updatePermission(ShiroFilterFactoryBean shiroFilterFactoryBean, Integer roleId, Boolean isRemoveSession) { + + } + + @Override + public void updatePermissionByRoleId(Integer roleId, Boolean isRemoveSession) { + + } +} diff --git a/backend/src/main/java/io/dataease/auth/util/JWTUtils.java b/backend/src/main/java/io/dataease/auth/util/JWTUtils.java new file mode 100644 index 0000000000..48bb10f1e2 --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/util/JWTUtils.java @@ -0,0 +1,83 @@ +package io.dataease.auth.util; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTDecodeException; +import com.auth0.jwt.interfaces.DecodedJWT; + +import java.util.Date; + +public class JWTUtils { + + + // 过期时间5分钟 + private static final long EXPIRE_TIME = 5*60*1000; + + + + /** + * 校验token是否正确 + * @param token 密钥 + * @param secret 用户的密码 + * @return 是否正确 + */ + public static boolean verify(String token, String username, String secret) { + Algorithm algorithm = Algorithm.HMAC256(secret); + JWTVerifier verifier = JWT.require(algorithm) + .withClaim("username", username) + .build(); + verifier.verify(token); + return true; + } + + /** + * 获得token中的信息无需secret解密也能获得 + * @return token中包含的用户名 + */ + public static String getUsername(String token) { + try { + DecodedJWT jwt = JWT.decode(token); + return jwt.getClaim("username").asString(); + } catch (JWTDecodeException e) { + return null; + } + } + + + + public static boolean needRefresh(String token){ + Date exp = JWTUtils.getExp(token); + return new Date().getTime() >= exp.getTime(); + } + + public static Date getExp(String token) { + try { + DecodedJWT jwt = JWT.decode(token); + return jwt.getClaim("exp").asDate(); + } catch (JWTDecodeException e) { + return null; + } + } + + /** + * 生成签名,5min后过期 + * @param username 用户名 + * @param secret 用户的密码 + * @return 加密的token + */ + public static String sign(String username, String secret) { + try { + Date date = new Date(System.currentTimeMillis()+EXPIRE_TIME); + Algorithm algorithm = Algorithm.HMAC256(secret); + // 附带username信息 + return JWT.create() + .withClaim("username", username) + .withClaim("exp", date) + .withExpiresAt(date) + .sign(algorithm); + } catch (Exception e) { + return null; + } + } +} diff --git a/frontend/src/utils/auth.js b/frontend/src/utils/auth.js new file mode 100644 index 0000000000..67539b1258 --- /dev/null +++ b/frontend/src/utils/auth.js @@ -0,0 +1,23 @@ +import Cookies from 'js-cookie' + + +const TokenKey = 'Authorization' +const tokenCookieExpires = 1 + +export function getToken() { + return Cookies.get(TokenKey) +} + +export function setToken(token, rememberMe) { + if (rememberMe) { + return Cookies.set(TokenKey, token, { expires: tokenCookieExpires }) + } else return Cookies.set(TokenKey, token) +} + +export function tokenKey(){ + return TokenKey +} + +export function removeToken() { + return Cookies.remove(TokenKey) +}