diff --git a/README.md b/README.md index 4bd3947..fcaf458 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,9 @@ forest([ˈfôrəst],n.森林)是一款现代化的知识社区项目,使 ## 鸣谢 - 感谢 `JetBrains` 对本项目的帮助,为作者提供了开源许可版 `JetBrains` 全家桶 -![JetBrains](src/main/resources/static/jetbrains.png) + +![JetBrains](src/main/resources/static/jb_beam.svg) + ## ⭐ Star 历史 diff --git a/pom.xml b/pom.xml index 90e594e..b9014d4 100644 --- a/pom.xml +++ b/pom.xml @@ -128,7 +128,7 @@ com.alibaba fastjson - 2.0.15 + 2.0.16 @@ -147,6 +147,12 @@ commons-collections4 4.4 + + + com.github.f4b6a3 + ulid-creator + 5.1.0 + commons-lang @@ -177,7 +183,7 @@ com.alibaba druid-spring-boot-starter - 1.2.13-SNSAPSHOT + 1.2.14 org.apache.logging.log4j @@ -255,12 +261,12 @@ cn.hutool hutool-core - 5.8.8 + 5.8.9 cn.hutool hutool-http - 5.8.8 + 5.8.9 diff --git a/src/main/java/com/rymcu/forest/auth/BaseHashedCredentialsMatcher.java b/src/main/java/com/rymcu/forest/auth/BaseHashedCredentialsMatcher.java new file mode 100644 index 0000000..9b82351 --- /dev/null +++ b/src/main/java/com/rymcu/forest/auth/BaseHashedCredentialsMatcher.java @@ -0,0 +1,31 @@ +package com.rymcu.forest.auth; + +import com.rymcu.forest.dto.TokenUser; +import com.rymcu.forest.util.UserUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.credential.HashedCredentialsMatcher; +import org.apache.shiro.authz.UnauthenticatedException; + +import java.util.Objects; + +/** + * @author ronger + */ +public class BaseHashedCredentialsMatcher extends HashedCredentialsMatcher { + + @Override + public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { + TokenModel tokenModel = (TokenModel) token; + String accessToken = (String) tokenModel.getCredentials(); + if (StringUtils.isBlank(accessToken)) { + throw new UnauthenticatedException(); + } + TokenUser tokenUser = UserUtils.getTokenUser(accessToken); + if (Objects.isNull(tokenUser)) { + throw new UnauthenticatedException(); + } + return true; + } +} diff --git a/src/main/java/com/rymcu/forest/jwt/def/JwtConstants.java b/src/main/java/com/rymcu/forest/auth/JwtConstants.java similarity index 73% rename from src/main/java/com/rymcu/forest/jwt/def/JwtConstants.java rename to src/main/java/com/rymcu/forest/auth/JwtConstants.java index 62162fe..01cf63b 100644 --- a/src/main/java/com/rymcu/forest/jwt/def/JwtConstants.java +++ b/src/main/java/com/rymcu/forest/auth/JwtConstants.java @@ -1,21 +1,26 @@ -package com.rymcu.forest.jwt.def; +package com.rymcu.forest.auth; /** * @author ronger */ public class JwtConstants { + + /** * 上线需要变更 */ public static final String JWT_SECRET = "JYJ5Qv2WF4lA6jPl5GKuAG"; - + public static final String AUTHORIZATION = "Authorization"; public static final String UPLOAD_TOKEN = "X-Upload-Token"; public static final String CURRENT_USER_NAME = "CURRENT_TOKEN_USER_NAME"; public static final String CURRENT_TOKEN_CLAIMS = "CURRENT_TOKEN_CLAIMS"; public static final String LAST_ONLINE = "last_online:"; - public static final long TOKEN_EXPIRES_HOUR = 2 * 60; + + public static final long TOKEN_EXPIRES_HOUR = 2; public static final long LAST_ONLINE_EXPIRES_MINUTE = 10; + public static final long TOKEN_EXPIRES_MINUTE = 15; + public static final long REFRESH_TOKEN_EXPIRES_HOUR = 2; } diff --git a/src/main/java/com/rymcu/forest/auth/JwtFilter.java b/src/main/java/com/rymcu/forest/auth/JwtFilter.java new file mode 100644 index 0000000..f184811 --- /dev/null +++ b/src/main/java/com/rymcu/forest/auth/JwtFilter.java @@ -0,0 +1,118 @@ +package com.rymcu.forest.auth; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureException; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.authz.UnauthenticatedException; +import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter; +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; +import java.util.Objects; + +/** + * Created on 2022/10/27 19:58. + * + * @author ronger + * @email ronger-x@outlook.com + * @desc : com.rymcu.forest.auth + */ +@Slf4j +public class JwtFilter extends BasicHttpAuthenticationFilter { + /** + * 判断用户是否想要登入。 + * 检测 header 里面是否包含 Authorization 字段即可 + */ + @Override + protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) { + HttpServletRequest req = (HttpServletRequest) request; + String authorization = req.getHeader(JwtConstants.AUTHORIZATION); + return authorization != null; + } + + /** + * + */ + @Override + protected boolean executeLogin(ServletRequest request, ServletResponse response) { + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + String authorization = httpServletRequest.getHeader(JwtConstants.AUTHORIZATION); + // 验证token + Claims claims; + try { + claims = Jwts.parser().setSigningKey(JwtConstants.JWT_SECRET).parseClaimsJws(authorization).getBody(); + } catch (final SignatureException e) { + throw new UnauthenticatedException(); + } + Object username = claims.getId(); + if (Objects.isNull(username)) { + throw new UnauthenticatedException(); + } + TokenModel token = new TokenModel(username.toString(), authorization); + // 提交给realm进行登入,如果错误他会抛出异常并被捕获 + getSubject(request, response).login(token); + // 如果没有抛出异常则代表登入成功,返回true + return true; + } + + /** + * 这里我们详细说明下为什么最终返回的都是true,即允许访问 + * 例如我们提供一个地址 GET /article + * 登入用户和游客看到的内容是不同的 + * 如果在这里返回了false,请求会被直接拦截,用户看不到任何东西 + * 所以我们在这里返回true,Controller中可以通过 subject.isAuthenticated() 来判断用户是否登入 + * 如果有些资源只有登入用户才能访问,我们只需要在方法上面加上 @RequiresAuthentication 注解即可 + * 但是这样做有一个缺点,就是不能够对GET,POST等请求进行分别过滤鉴权(因为我们重写了官方的方法),但实际上对应用影响不大 + */ + @Override + protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { + if (isLoginAttempt(request, response)) { + try { + executeLogin(request, response); + } catch (Exception e) { + response401(request, response); + } + } + return true; + } + + /** + * 对跨域提供支持 + */ + @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 request, ServletResponse response) { + try { + HttpServletResponse httpResponse = (HttpServletResponse) response; + httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + httpResponse.setContentType("application/json;charset=utf-8"); + httpResponse.getOutputStream().write("login fail".getBytes()); + } catch (IOException e) { + // 错误日志 + log.error(e.getMessage()); + } + } +} diff --git a/src/main/java/com/rymcu/forest/auth/JwtRealm.java b/src/main/java/com/rymcu/forest/auth/JwtRealm.java new file mode 100644 index 0000000..859898e --- /dev/null +++ b/src/main/java/com/rymcu/forest/auth/JwtRealm.java @@ -0,0 +1,75 @@ +package com.rymcu.forest.auth; + +import com.rymcu.forest.dto.TokenUser; +import com.rymcu.forest.entity.Role; +import com.rymcu.forest.entity.User; +import com.rymcu.forest.service.RoleService; +import com.rymcu.forest.util.UserUtils; +import org.apache.commons.lang3.StringUtils; +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 javax.annotation.Resource; +import java.util.List; + +/** + * Created on 2022/10/27 20:04. + * + * @author ronger + * @email ronger-x@outlook.com + * @desc : com.rymcu.forest.auth + */ +public class JwtRealm extends AuthorizingRealm { + @Resource + private RoleService roleService; + + @Resource + private TokenManager manager; + + + @Override + public boolean supports(AuthenticationToken token) { + return token instanceof TokenModel; + } + + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { + String accessToken = (String) principals.getPrimaryPrincipal(); + TokenUser tokenUser = UserUtils.getTokenUser(accessToken); + SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); + User user = new User(); + user.setIdUser(tokenUser.getIdUser()); + try { + List roles = roleService.selectRoleByUser(user); + for (Role role : roles) { + if (StringUtils.isNotBlank(role.getInputCode())) { + authorizationInfo.addRole(role.getInputCode()); + authorizationInfo.addStringPermission(role.getInputCode()); + } + } + // 添加用户权限 + authorizationInfo.addStringPermission("user"); + } catch (Exception e) { + e.printStackTrace(); + } + return authorizationInfo; + } + + /** + * 认证回调函数, 登录时调用,主要是用来进行身份认证的,也就是说验证用户输入的账号和密码是否正确。 + */ + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) throws AuthenticationException { + TokenModel token = (TokenModel) authToken; + if (!manager.checkToken(token)) { + throw new AuthenticationException(); + } + return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), this.getName()); + } +} \ No newline at end of file diff --git a/src/main/java/com/rymcu/forest/jwt/service/RedisTokenManager.java b/src/main/java/com/rymcu/forest/auth/RedisTokenManager.java similarity index 86% rename from src/main/java/com/rymcu/forest/jwt/service/RedisTokenManager.java rename to src/main/java/com/rymcu/forest/auth/RedisTokenManager.java index a409a01..a5a7f80 100644 --- a/src/main/java/com/rymcu/forest/jwt/service/RedisTokenManager.java +++ b/src/main/java/com/rymcu/forest/auth/RedisTokenManager.java @@ -1,9 +1,7 @@ -package com.rymcu.forest.jwt.service; +package com.rymcu.forest.auth; import com.rymcu.forest.handler.event.AccountEvent; -import com.rymcu.forest.jwt.def.JwtConstants; -import com.rymcu.forest.jwt.model.TokenModel; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.apache.commons.lang.StringUtils; @@ -38,7 +36,7 @@ public class RedisTokenManager implements TokenManager { //使用 account 作为源 token String token = Jwts.builder().setId(id).setSubject(id).setIssuedAt(new Date()).signWith(SignatureAlgorithm.HS256, JwtConstants.JWT_SECRET).compact(); //存储到 redis 并设置过期时间 - redisTemplate.boundValueOps(id).set(token, JwtConstants.TOKEN_EXPIRES_HOUR, TimeUnit.HOURS); + redisTemplate.boundValueOps(id).set(token, JwtConstants.TOKEN_EXPIRES_MINUTE, TimeUnit.MINUTES); return token; } @@ -56,8 +54,6 @@ public class RedisTokenManager implements TokenManager { if (token == null || !token.equals(model.getToken())) { return false; } - // 如果验证成功,说明此用户进行了一次有效操作,延长 token 的过期时间 - redisTemplate.boundValueOps(model.getUsername()).expire(JwtConstants.TOKEN_EXPIRES_HOUR, TimeUnit.HOURS); StringBuilder key = new StringBuilder(); key.append(JwtConstants.LAST_ONLINE).append(model.getUsername()); String result = redisTemplate.boundValueOps(key.toString()).get(); diff --git a/src/main/java/com/rymcu/forest/jwt/service/TokenManager.java b/src/main/java/com/rymcu/forest/auth/TokenManager.java similarity index 87% rename from src/main/java/com/rymcu/forest/jwt/service/TokenManager.java rename to src/main/java/com/rymcu/forest/auth/TokenManager.java index 66ac47d..5b62e0c 100644 --- a/src/main/java/com/rymcu/forest/jwt/service/TokenManager.java +++ b/src/main/java/com/rymcu/forest/auth/TokenManager.java @@ -1,7 +1,4 @@ -package com.rymcu.forest.jwt.service; - - -import com.rymcu.forest.jwt.model.TokenModel; +package com.rymcu.forest.auth; /** * 对token进行操作的接口 @@ -12,6 +9,7 @@ public interface TokenManager { /** * 创建一个token关联上指定用户 + * @param id * @return 生成的token */ public String createToken(String id); @@ -25,6 +23,8 @@ public interface TokenManager { /** * 从字符串中解析token + * @param token + * @param account * @return */ public TokenModel getToken(String token, String account); diff --git a/src/main/java/com/rymcu/forest/jwt/model/TokenModel.java b/src/main/java/com/rymcu/forest/auth/TokenModel.java similarity index 61% rename from src/main/java/com/rymcu/forest/jwt/model/TokenModel.java rename to src/main/java/com/rymcu/forest/auth/TokenModel.java index 55918c6..beb44ad 100644 --- a/src/main/java/com/rymcu/forest/jwt/model/TokenModel.java +++ b/src/main/java/com/rymcu/forest/auth/TokenModel.java @@ -1,16 +1,22 @@ -package com.rymcu.forest.jwt.model; +package com.rymcu.forest.auth; + +import org.apache.shiro.authc.AuthenticationToken; /** * Token的Model类,可以增加字段提高安全性,例如时间戳、url签名 * @author ScienJus * @date 2015/7/31. */ -public class TokenModel { +public class TokenModel implements AuthenticationToken { private String username; private String token; + public TokenModel(String token) { + this.token = token; + } + public TokenModel(String username, String token) { this.username = username; this.token = token; @@ -31,4 +37,14 @@ public class TokenModel { public void setToken(String token) { this.token = token; } + + @Override + public Object getPrincipal() { + return token; + } + + @Override + public Object getCredentials() { + return token; + } } diff --git a/src/main/java/com/rymcu/forest/config/BaseSessionManager.java b/src/main/java/com/rymcu/forest/config/BaseSessionManager.java deleted file mode 100644 index 7d76213..0000000 --- a/src/main/java/com/rymcu/forest/config/BaseSessionManager.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.rymcu.forest.config; - -import org.apache.shiro.web.servlet.ShiroHttpServletRequest; -import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; -import org.apache.shiro.web.util.WebUtils; -import org.springframework.util.StringUtils; - -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import java.io.Serializable; - -/** - * @author ronger - */ -public class BaseSessionManager extends DefaultWebSessionManager { - private static final String AUTHORIZATION = "Authorization"; - - private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request"; - - public BaseSessionManager() { - super(); - } - - @Override - protected Serializable getSessionId(ServletRequest request, ServletResponse response) { - String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION); - // 如果请求头中有 Authorization 则其值为 sessionId - if (!StringUtils.isEmpty(id)) { - request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE); - request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id); - request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); - - return id; - } else { - // 否则按默认规则从 cookie 取 sessionId - return super.getSessionId(request, response); - } - } -} diff --git a/src/main/java/com/rymcu/forest/config/BaseShiroFilterFactoryBean.java b/src/main/java/com/rymcu/forest/config/BaseShiroFilterFactoryBean.java deleted file mode 100644 index 8215885..0000000 --- a/src/main/java/com/rymcu/forest/config/BaseShiroFilterFactoryBean.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.rymcu.forest.config; - -import org.apache.shiro.mgt.SecurityManager; -import org.apache.shiro.spring.web.ShiroFilterFactoryBean; -import org.apache.shiro.web.filter.mgt.FilterChainManager; -import org.apache.shiro.web.filter.mgt.FilterChainResolver; -import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver; -import org.apache.shiro.web.mgt.WebSecurityManager; -import org.apache.shiro.web.servlet.AbstractShiroFilter; -import org.springframework.beans.factory.BeanInitializationException; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -/** - * @author ronger - * Shiro静态资源配置 - * */ -public class BaseShiroFilterFactoryBean extends ShiroFilterFactoryBean { - - private Set ignoreExt; - - public BaseShiroFilterFactoryBean() { - super(); - ignoreExt = new HashSet<>(); - ignoreExt.add(".svg"); - ignoreExt.add(".jpg"); - ignoreExt.add(".png"); - ignoreExt.add(".gif"); - ignoreExt.add(".bmp"); - ignoreExt.add(".js"); - ignoreExt.add(".css"); - } - - @Override - protected AbstractShiroFilter createInstance() throws Exception { - - SecurityManager securityManager = getSecurityManager(); - if (securityManager == null) { - String msg = "SecurityManager property must be set."; - throw new BeanInitializationException(msg); - } - - if (!(securityManager instanceof WebSecurityManager)) { - String msg = "The security manager does not implement the WebSecurityManager interface."; - throw new BeanInitializationException(msg); - } - - FilterChainManager manager = createFilterChainManager(); - - PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver(); - chainResolver.setFilterChainManager(manager); - - return new BaseSpringShiroFilter((WebSecurityManager) securityManager, chainResolver); - } - - private final class BaseSpringShiroFilter extends AbstractShiroFilter { - - protected BaseSpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) { - super(); - if (webSecurityManager == null) { - throw new IllegalArgumentException("WebSecurityManager property cannot be null."); - } - setSecurityManager(webSecurityManager); - if (resolver != null) { - setFilterChainResolver(resolver); - } - } - - @Override - protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, - FilterChain chain) throws ServletException, IOException { - HttpServletRequest request = (HttpServletRequest)servletRequest; - String str = request.getRequestURI().toLowerCase(); - // 因为ShiroFilter 拦截所有请求(在上面我们配置了urlPattern 为 * ,当然你也可以在那里精确的添加要处理的路径,这样就不需要这个类了),而在每次请求里面都做了session的读取和更新访问时间等操作,这样在集群部署session共享的情况下,数量级的加大了处理量负载。 - // 所以我们这里将一些能忽略的请求忽略掉。 - // 当然如果你的集群系统使用了动静分离处理,静态资料的请求不会到Filter这个层面,便可以忽略。 - boolean flag = true; - int idx = 0; - if(( idx = str.indexOf(".")) > 0){ - str = str.substring(idx); - if(ignoreExt.contains(str.toLowerCase())) { - flag = false; - } - } - if(flag){ - super.doFilterInternal(servletRequest, servletResponse, chain); - }else{ - chain.doFilter(servletRequest, servletResponse); - } - } - - } -} diff --git a/src/main/java/com/rymcu/forest/config/BaseShiroRealm.java b/src/main/java/com/rymcu/forest/config/BaseShiroRealm.java deleted file mode 100644 index 2ddf682..0000000 --- a/src/main/java/com/rymcu/forest/config/BaseShiroRealm.java +++ /dev/null @@ -1,158 +0,0 @@ -package com.rymcu.forest.config; - -import com.fasterxml.jackson.annotation.JsonFormat; -import com.rymcu.forest.core.constant.ShiroConstants; -import com.rymcu.forest.core.exception.CaptchaException; -import com.rymcu.forest.entity.Permission; -import com.rymcu.forest.entity.Role; -import com.rymcu.forest.entity.User; -import com.rymcu.forest.service.PermissionService; -import com.rymcu.forest.service.RoleService; -import com.rymcu.forest.service.UserService; -import com.rymcu.forest.util.Encodes; -import com.rymcu.forest.util.Utils; -import org.apache.commons.lang3.StringUtils; -import org.apache.ibatis.exceptions.TooManyResultsException; -import org.apache.shiro.authc.*; -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.apache.shiro.util.ByteSource; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -import javax.annotation.Resource; -import java.io.Serializable; -import java.util.List; - -/** - * @author ronger - * @since 2018/05/28 11:00 - * 自定义权限匹配和账号密码匹配 - */ -public class BaseShiroRealm extends AuthorizingRealm { - @Resource - private RoleService roleService; - - @Resource - private PermissionService permissionService; - - @Resource - private UserService userService; - - @Override - protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { - SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); - Principal principal = (Principal) principals.getPrimaryPrincipal(); - User user = new User(); - user.setIdUser(principal.getId()); - try { - List roles = roleService.selectRoleByUser(user); - for (Role role : roles) { - if (StringUtils.isNotBlank(role.getInputCode())) { - authorizationInfo.addRole(role.getInputCode()); - } - } - List permissions = permissionService.selectPermissionByUser(user); - for (Permission perm : permissions) { - if (perm.getPermissionCategory() != null) { - authorizationInfo.addStringPermission(perm.getPermissionCategory()); - } - } - // 添加用户权限 - authorizationInfo.addStringPermission("user"); - - } catch (Exception e) { - e.printStackTrace(); - } - return authorizationInfo; - } - - /** - * 认证回调函数, 登录时调用,主要是用来进行身份认证的,也就是说验证用户输入的账号和密码是否正确。 - */ - @Override - protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { - UsernamePasswordToken token = (UsernamePasswordToken) authcToken; - //获取用户的输入的账号. - String username = token.getUsername(); - - User user = null; - ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); - if (!org.springframework.util.StringUtils.isEmpty(attributes.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA))) { - throw new CaptchaException(); - } - try { - user = userService.findByAccount(username); - } catch (TooManyResultsException e) { - e.printStackTrace(); - } - if (user == null) { - return null; - } - // 账户冻结(是否允许登陆) - if (!"0".equals(user.getStatus())) { - throw new LockedAccountException(); - } - byte[] salt = Encodes.decodeHex(user.getPassword().substring(0, 16)); - return new SimpleAuthenticationInfo(new Principal(user, token.isMobileLogin()), - user.getPassword().substring(16), ByteSource.Util.bytes(salt), getName()); - } - - /** - * 授权用户信息 - */ - public static class Principal implements Serializable { - - private static final long serialVersionUID = 1L; - - @JsonFormat(shape = JsonFormat.Shape.STRING) - private Long id; // 编号 - private String account; // 登录名 - private String name; // 姓名 - private boolean mobileLogin; // 是否手机登录 - -// private Map cacheMap; - - public Principal(User user, boolean mobileLogin) { - this.id = user.getIdUser(); - this.account = user.getAccount(); - this.name = user.getNickname(); - this.mobileLogin = mobileLogin; - } - - public Long getId() { - return id; - } - - public String getAccount() { - return account; - } - - public String getName() { - return name; - } - - public boolean isMobileLogin() { - return mobileLogin; - } - - /** - * 获取SESSIONID - */ - public String getSessionid() { - try { - return (String) Utils.getSession().getId(); - } catch (Exception e) { - return ""; - } - } - - @Override - public String toString() { - return id.toString(); - } - - } -} diff --git a/src/main/java/com/rymcu/forest/config/MybatisConfigurer.java b/src/main/java/com/rymcu/forest/config/MybatisConfigurer.java index 73aede7..7292eda 100644 --- a/src/main/java/com/rymcu/forest/config/MybatisConfigurer.java +++ b/src/main/java/com/rymcu/forest/config/MybatisConfigurer.java @@ -1,7 +1,6 @@ package com.rymcu.forest.config; import com.github.pagehelper.PageInterceptor; -import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.context.annotation.Bean; diff --git a/src/main/java/com/rymcu/forest/config/RedisKeyExpirationListener.java b/src/main/java/com/rymcu/forest/config/RedisKeyExpirationListener.java index ce81763..7b358e0 100644 --- a/src/main/java/com/rymcu/forest/config/RedisKeyExpirationListener.java +++ b/src/main/java/com/rymcu/forest/config/RedisKeyExpirationListener.java @@ -1,7 +1,6 @@ package com.rymcu.forest.config; -import com.rymcu.forest.entity.User; -import com.rymcu.forest.jwt.def.JwtConstants; +import com.rymcu.forest.auth.JwtConstants; import com.rymcu.forest.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -40,14 +39,13 @@ public class RedisKeyExpirationListener extends KeyExpirationEventMessageListene */ @Override public void onMessage(Message message, byte[] pattern) { - // 获取到失效的 key String expiredKey = message.toString(); if (expiredKey.contains(JwtConstants.LAST_ONLINE)) { - String email = expiredKey.replace(JwtConstants.LAST_ONLINE, ""); + String account = expiredKey.replace(JwtConstants.LAST_ONLINE, ""); log.info("拿到过期的数据:{}", expiredKey); - log.info("处理后的数据:{}", email); - userService.updateLastOnlineTimeByEmail(email); + log.info("处理后的数据:{}", account); + userService.updateLastOnlineTimeByAccount(account); } super.onMessage(message, pattern); } diff --git a/src/main/java/com/rymcu/forest/config/ShiroConfig.java b/src/main/java/com/rymcu/forest/config/ShiroConfig.java index 47463c3..2c78f14 100644 --- a/src/main/java/com/rymcu/forest/config/ShiroConfig.java +++ b/src/main/java/com/rymcu/forest/config/ShiroConfig.java @@ -1,20 +1,17 @@ package com.rymcu.forest.config; -import org.apache.shiro.authc.credential.HashedCredentialsMatcher; +import com.rymcu.forest.auth.BaseHashedCredentialsMatcher; +import com.rymcu.forest.auth.JwtFilter; +import com.rymcu.forest.auth.JwtRealm; import org.apache.shiro.mgt.SecurityManager; -import org.apache.shiro.session.mgt.SessionManager; -import org.apache.shiro.session.mgt.eis.MemorySessionDAO; 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.filter.authc.FormAuthenticationFilter; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.EnvironmentAware; +import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; +import org.springframework.context.annotation.DependsOn; import javax.servlet.Filter; import java.util.LinkedHashMap; @@ -24,108 +21,46 @@ import java.util.Map; * @author ronger */ @Configuration -@ConfigurationProperties(prefix = "redis.shiro") -public class ShiroConfig implements EnvironmentAware { - - private Environment env; - - @Override - public void setEnvironment(Environment environment) { - this.env=environment; - } +public class ShiroConfig { @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { - ShiroFilterFactoryBean shiroFilterFactoryBean = new BaseShiroFilterFactoryBean(); + ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); + //添加自定义的过滤器 + LinkedHashMap linkedHashMap = new LinkedHashMap<>(); + linkedHashMap.put("jwt", new JwtFilter()); + shiroFilterFactoryBean.setFilters(linkedHashMap); Map filterChainDefinitionMap = new LinkedHashMap(); - //注意过滤器配置顺序 不能颠倒 - //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了,登出后跳转配置的loginUrl - filterChainDefinitionMap.put("/logout", "logout"); - // 配置不会被拦截的链接 顺序判断 - filterChainDefinitionMap.put("/druid/**", "anon"); - filterChainDefinitionMap.put("/css/**", "anon"); - filterChainDefinitionMap.put("/fonts/**", "anon"); - filterChainDefinitionMap.put("/js/**", "anon"); - filterChainDefinitionMap.put("/uploadFile/**", "anon"); - filterChainDefinitionMap.put("/login", "anon"); - - filterChainDefinitionMap.put("/api/**", "anon"); - filterChainDefinitionMap.put("/ws/**", "anon"); - filterChainDefinitionMap.put("/wss/**", "anon"); - filterChainDefinitionMap.put("/wx/**", "anon"); - filterChainDefinitionMap.put("/**", "auth"); - //配置shiro默认登录界面地址,前后端分离中登录界面跳转应由前端路由控制,后台仅返回json数据 - shiroFilterFactoryBean.setLoginUrl("/login"); - // 登录成功后要跳转的链接 - shiroFilterFactoryBean.setSuccessUrl("/index"); - //未授权界面; - shiroFilterFactoryBean.setUnauthorizedUrl("/403"); - shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); - - Map filtersMap = new LinkedHashMap<>(); - filtersMap.put("auth", baseFormAuthenticationFilter()); - shiroFilterFactoryBean.setFilters(filtersMap); - - filterChainDefinitionMap.put("/**", "auth"); + filterChainDefinitionMap.put("/api/v1/console/**", "anon"); + filterChainDefinitionMap.put("/api/v1/open-data/**", "anon"); + filterChainDefinitionMap.put("/**", "jwt"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } - /** - * 凭证匹配器 - * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 - * ) - * - * @return - */ @Bean - public HashedCredentialsMatcher hashedCredentialsMatcher() { - HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); - // 散列算法:这里使用MD5算法; - hashedCredentialsMatcher.setHashAlgorithmName("SHA-1"); - // 散列的次数,比如散列两次,相当于 md5(md5("")); - hashedCredentialsMatcher.setHashIterations(1024); - return hashedCredentialsMatcher; - } - - @Bean - public BaseShiroRealm baseShiroRealm() { - BaseShiroRealm shiroRealm = new BaseShiroRealm(); - shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); + public JwtRealm baseShiroRealm() { + JwtRealm shiroRealm = new JwtRealm(); + shiroRealm.setCredentialsMatcher(new BaseHashedCredentialsMatcher()); return shiroRealm; } - - @Bean - public SecurityManager securityManager() { - DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); - securityManager.setRealm(baseShiroRealm()); - // 自定义session管理 使用redis - securityManager.setSessionManager(sessionManager()); - return securityManager; - } - - /** - * 自定义sessionManager + /*** + * 配置安全管理器 * */ @Bean - public SessionManager sessionManager() { - BaseSessionManager sessionManager = new BaseSessionManager(); - sessionManager.setSessionDAO(new MemorySessionDAO()); - sessionManager.setSessionIdUrlRewritingEnabled(false); - sessionManager.setGlobalSessionTimeout(21600000L); - return sessionManager; + public SecurityManager securityManager() { + DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); + defaultWebSecurityManager.setRealm(baseShiroRealm()); + return defaultWebSecurityManager; } /** * 开启shiro aop注解支持. * 使用代理方式;所以需要开启代码支持; - * - * @param securityManager - * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { @@ -136,37 +71,17 @@ public class ShiroConfig implements EnvironmentAware { /** * Shiro生命周期处理器 - * @return */ @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){ return new LifecycleBeanPostProcessor(); } - /** - * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 - * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能 - * - * @return - */ @Bean - public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { - AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); - authorizationAttributeSourceAdvisor.setSecurityManager(securityManager()); - return authorizationAttributeSourceAdvisor; - } - - public FormAuthenticationFilter baseFormAuthenticationFilter(){ - FormAuthenticationFilter formAuthenticationFilter = new ShiroLoginFilter(); - return formAuthenticationFilter; - } - - @Bean - public FilterRegistrationBean someFilterRegistration() { - FilterRegistrationBean registration = new FilterRegistrationBean(); - FormAuthenticationFilter baseFormAuthenticationFilter = new ShiroLoginFilter(); - registration.setFilter(baseFormAuthenticationFilter); - registration.setEnabled(false); - return registration; + @DependsOn({"lifecycleBeanPostProcessor"}) + public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() { + DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); + advisorAutoProxyCreator.setProxyTargetClass(true); + return advisorAutoProxyCreator; } } diff --git a/src/main/java/com/rymcu/forest/config/ShiroLoginFilter.java b/src/main/java/com/rymcu/forest/config/ShiroLoginFilter.java deleted file mode 100644 index babf323..0000000 --- a/src/main/java/com/rymcu/forest/config/ShiroLoginFilter.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.rymcu.forest.config; - - -import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.serializer.SerializerFeature; -import com.rymcu.forest.core.result.GlobalResultGenerator; -import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * - * @author wanwh - * @date 2019/1/24 0024 - */ -public class ShiroLoginFilter extends FormAuthenticationFilter { - - private static final Logger log = LoggerFactory.getLogger(FormAuthenticationFilter.class); - - /** - * 在访问controller前判断是否登录,返回json,不进行重定向。 - * @param request - * @param response - * @return true-继续往下执行,false-该filter过滤器已经处理,不继续执行其他过滤器 - * @throws Exception - */ - - @Override - protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { - HttpServletResponse httpServletResponse = (HttpServletResponse) response; - if (this.isLoginRequest(request, response)) { - if (this.isLoginSubmission(request, response)) { - if (log.isTraceEnabled()) { - log.trace("Login submission detected. Attempting to execute login."); - } - - return this.executeLogin(request, response); - } else { - if (log.isTraceEnabled()) { - log.trace("Login page view."); - } - - return true; - } - }else if(isAjax((HttpServletRequest) request)){ - httpServletResponse.setContentType("application/json"); - httpServletResponse.setCharacterEncoding("UTF-8"); - httpServletResponse.setHeader("sessionstatus", "timeOut"); - httpServletResponse.addHeader("loginPath", this.getLoginUrl()); - httpServletResponse.getWriter().write(JSONObject.toJSONString(GlobalResultGenerator.genErrorResult("未登录或已登录超时,请重新登录"), SerializerFeature.PrettyFormat)); - return false; - }else { - if (log.isTraceEnabled()) { - log.trace("Attempting to access a path which requires authentication. Forwarding to the Authentication url [" + this.getLoginUrl() + "]"); - } - - this.saveRequestAndRedirectToLogin(request, response); - return false; - } - } - - private boolean isAjax(HttpServletRequest request) { - String requestedWith = request.getHeader("x-requested-with"); - if (requestedWith != null && requestedWith.equalsIgnoreCase("XMLHttpRequest")) { - return true; - } else { - return false; - } - } -} diff --git a/src/main/java/com/rymcu/forest/config/UsernamePasswordToken.java b/src/main/java/com/rymcu/forest/config/UsernamePasswordToken.java deleted file mode 100644 index 3225b52..0000000 --- a/src/main/java/com/rymcu/forest/config/UsernamePasswordToken.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.rymcu.forest.config; - -/** - * 用户和密码(包含验证码)令牌类 - * @author ronger - */ -public class UsernamePasswordToken extends org.apache.shiro.authc.UsernamePasswordToken { - - private static final long serialVersionUID = 1L; - - private String captcha; - private boolean mobileLogin; - - public UsernamePasswordToken() { - super(); - } - - public UsernamePasswordToken(String username, char[] password, - boolean rememberMe, String host, String captcha, boolean mobileLogin) { - super(username, password, rememberMe, host); - this.captcha = captcha; - this.mobileLogin = mobileLogin; - } - - public String getCaptcha() { - return captcha; - } - - public void setCaptcha(String captcha) { - this.captcha = captcha; - } - - public boolean isMobileLogin() { - return mobileLogin; - } - -} \ No newline at end of file diff --git a/src/main/java/com/rymcu/forest/config/WebMvcConfigurer.java b/src/main/java/com/rymcu/forest/config/WebMvcConfigurer.java index b14d7d2..30c9cbe 100644 --- a/src/main/java/com/rymcu/forest/config/WebMvcConfigurer.java +++ b/src/main/java/com/rymcu/forest/config/WebMvcConfigurer.java @@ -4,17 +4,14 @@ package com.rymcu.forest.config; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; -import com.rymcu.forest.jwt.aop.RestAuthTokenInterceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.util.ResourceUtils; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; @@ -60,22 +57,6 @@ public class WebMvcConfigurer extends WebMvcConfigurationSupport { .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH"); } - @Bean - public RestAuthTokenInterceptor restAuthTokenInterceptor() { - return new RestAuthTokenInterceptor(); - } - - /** - * 添加拦截器 - */ - @Override - public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(restAuthTokenInterceptor()).addPathPatterns("/api/**") - .excludePathPatterns("/api/v1/console/**", "/api/v1/article/articles/**", "/api/v1/article/detail/**" - , "/api/v1/topic/**", "/api/v1/user/**", "/api/v1/article/*/comments", "/api/v1/rule/currency/**", "/api/v1/lucene/**", "/api/v1/open-data/**"); - - } - /** * 访问静态资源 */ diff --git a/src/main/java/com/rymcu/forest/core/service/log/TransactionAspect.java b/src/main/java/com/rymcu/forest/core/service/log/TransactionAspect.java index 0f017c0..9da7487 100644 --- a/src/main/java/com/rymcu/forest/core/service/log/TransactionAspect.java +++ b/src/main/java/com/rymcu/forest/core/service/log/TransactionAspect.java @@ -2,12 +2,10 @@ package com.rymcu.forest.core.service.log; import com.rymcu.forest.core.result.GlobalResult; import com.rymcu.forest.core.service.log.annotation.TransactionLogger; -import com.rymcu.forest.entity.TransactionRecord; import com.rymcu.forest.entity.User; import com.rymcu.forest.enumerate.TransactionEnum; import com.rymcu.forest.service.TransactionRecordService; import com.rymcu.forest.util.UserUtils; -import com.rymcu.forest.web.api.exception.BaseApiException; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; @@ -15,11 +13,8 @@ import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.Objects; diff --git a/src/main/java/com/rymcu/forest/core/service/log/VisitAspect.java b/src/main/java/com/rymcu/forest/core/service/log/VisitAspect.java index e1c9c26..e7df59f 100644 --- a/src/main/java/com/rymcu/forest/core/service/log/VisitAspect.java +++ b/src/main/java/com/rymcu/forest/core/service/log/VisitAspect.java @@ -1,9 +1,9 @@ package com.rymcu.forest.core.service.log; +import com.rymcu.forest.auth.JwtConstants; import com.rymcu.forest.core.service.log.constant.LoggerConstant; import com.rymcu.forest.dto.TokenUser; import com.rymcu.forest.entity.Visit; -import com.rymcu.forest.jwt.def.JwtConstants; import com.rymcu.forest.service.ArticleService; import com.rymcu.forest.service.VisitService; import com.rymcu.forest.util.UserUtils; diff --git a/src/main/java/com/rymcu/forest/core/service/security/AuthorshipAspect.java b/src/main/java/com/rymcu/forest/core/service/security/AuthorshipAspect.java index a344aea..e4f6456 100644 --- a/src/main/java/com/rymcu/forest/core/service/security/AuthorshipAspect.java +++ b/src/main/java/com/rymcu/forest/core/service/security/AuthorshipAspect.java @@ -2,12 +2,12 @@ package com.rymcu.forest.core.service.security; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.rymcu.forest.auth.JwtConstants; import com.rymcu.forest.core.service.security.annotation.AuthorshipInterceptor; import com.rymcu.forest.dto.TokenUser; import com.rymcu.forest.entity.Article; import com.rymcu.forest.entity.Portfolio; import com.rymcu.forest.enumerate.Module; -import com.rymcu.forest.jwt.def.JwtConstants; import com.rymcu.forest.mapper.UserMapper; import com.rymcu.forest.service.ArticleService; import com.rymcu.forest.service.PortfolioService; @@ -76,7 +76,7 @@ public class AuthorshipAspect { } HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); String idArticle; - Long idAuthor = 0l; + Long idAuthor = 0L; if (isAjax(request)) { Object[] objects = joinPoint.getArgs(); JSONObject jsonObject; diff --git a/src/main/java/com/rymcu/forest/core/service/security/SecurityAspect.java b/src/main/java/com/rymcu/forest/core/service/security/SecurityAspect.java index 453d5eb..8a25025 100644 --- a/src/main/java/com/rymcu/forest/core/service/security/SecurityAspect.java +++ b/src/main/java/com/rymcu/forest/core/service/security/SecurityAspect.java @@ -2,8 +2,8 @@ package com.rymcu.forest.core.service.security; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.rymcu.forest.auth.JwtConstants; import com.rymcu.forest.dto.TokenUser; -import com.rymcu.forest.jwt.def.JwtConstants; import com.rymcu.forest.util.UserUtils; import com.rymcu.forest.web.api.exception.BaseApiException; import com.rymcu.forest.web.api.exception.ErrorCode; diff --git a/src/main/java/com/rymcu/forest/dto/SearchModel.java b/src/main/java/com/rymcu/forest/dto/SearchModel.java deleted file mode 100644 index 5a44e51..0000000 --- a/src/main/java/com/rymcu/forest/dto/SearchModel.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.rymcu.forest.dto; - -import lombok.Data; - -/** - * @author ronger - */ -@Data -public class SearchModel { - - private String label; - - private String value; - - private String type; - -} diff --git a/src/main/java/com/rymcu/forest/dto/TokenUser.java b/src/main/java/com/rymcu/forest/dto/TokenUser.java index 339e7bd..37ace1f 100644 --- a/src/main/java/com/rymcu/forest/dto/TokenUser.java +++ b/src/main/java/com/rymcu/forest/dto/TokenUser.java @@ -3,6 +3,8 @@ package com.rymcu.forest.dto; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; +import java.util.Set; + /** * @author ronger */ @@ -18,10 +20,10 @@ public class TokenUser { private String token; - private String avatarType; - private String avatarUrl; - private Integer weights; + private String refreshToken; + + private Set scope; } diff --git a/src/main/java/com/rymcu/forest/dto/UserInfoDTO.java b/src/main/java/com/rymcu/forest/dto/UserInfoDTO.java index e41efee..0978d60 100644 --- a/src/main/java/com/rymcu/forest/dto/UserInfoDTO.java +++ b/src/main/java/com/rymcu/forest/dto/UserInfoDTO.java @@ -4,7 +4,6 @@ import com.alibaba.fastjson.annotation.JSONField; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; -import javax.persistence.Column; import java.io.Serializable; import java.util.Date; diff --git a/src/main/java/com/rymcu/forest/dto/baidu/TagNlpDTO.java b/src/main/java/com/rymcu/forest/dto/baidu/TagNlpDTO.java deleted file mode 100644 index 14f156f..0000000 --- a/src/main/java/com/rymcu/forest/dto/baidu/TagNlpDTO.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.rymcu.forest.dto.baidu; - -import lombok.Data; - -import java.math.BigDecimal; - -/** - * @author ronger - */ -@Data -public class TagNlpDTO { - - private BigDecimal score; - - private String tag; - -} diff --git a/src/main/java/com/rymcu/forest/handler/AccountHandler.java b/src/main/java/com/rymcu/forest/handler/AccountHandler.java index 96315a5..75f6dde 100644 --- a/src/main/java/com/rymcu/forest/handler/AccountHandler.java +++ b/src/main/java/com/rymcu/forest/handler/AccountHandler.java @@ -1,11 +1,14 @@ package com.rymcu.forest.handler; import com.rymcu.forest.handler.event.AccountEvent; +import com.rymcu.forest.mapper.UserMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; +import javax.annotation.Resource; + /** * Created on 2022/8/24 14:44. * @@ -17,10 +20,13 @@ import org.springframework.stereotype.Component; @Component public class AccountHandler { + @Resource + private UserMapper userMapper; + @Async @EventListener - public void processAccountLastLoginEvent(AccountEvent accountEvent) { - + public void processAccountLastOnlineTimeEvent(AccountEvent accountEvent) { + userMapper.updateLastOnlineTimeByAccount(accountEvent.getAccount()); } } diff --git a/src/main/java/com/rymcu/forest/jwt/aop/RestAuthTokenInterceptor.java b/src/main/java/com/rymcu/forest/jwt/aop/RestAuthTokenInterceptor.java deleted file mode 100644 index ba42544..0000000 --- a/src/main/java/com/rymcu/forest/jwt/aop/RestAuthTokenInterceptor.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.rymcu.forest.jwt.aop; - - -import com.rymcu.forest.jwt.def.JwtConstants; -import com.rymcu.forest.jwt.model.TokenModel; -import com.rymcu.forest.jwt.service.TokenManager; -import com.rymcu.forest.mapper.UserMapper; -import com.rymcu.forest.web.api.exception.BaseApiException; -import com.rymcu.forest.web.api.exception.ErrorCode; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureException; -import org.apache.commons.lang.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.servlet.HandlerInterceptor; -import org.springframework.web.servlet.ModelAndView; - -import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.Objects; - -/** - * Restful请求, Token校验规则拦截器(JWT) - * - * @author scott - * @date 2015/7/30. - */ -public class RestAuthTokenInterceptor implements HandlerInterceptor { - - @Autowired - private TokenManager manager; - @Resource - private UserMapper userMapper; - - @Override - public void afterCompletion(HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse, Object obj, Exception exception) throws Exception { - // TODO Auto-generated method stub - } - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception { - //从header中得到token - String authHeader = request.getHeader(JwtConstants.AUTHORIZATION); - if(StringUtils.isBlank(authHeader)){ - authHeader = request.getHeader(JwtConstants.UPLOAD_TOKEN); - } - if (StringUtils.isBlank(authHeader)) { - throw new BaseApiException(ErrorCode.UNAUTHORIZED); - } - // 验证token - Claims claims = null; - try { - claims = Jwts.parser().setSigningKey(JwtConstants.JWT_SECRET).parseClaimsJws(authHeader).getBody(); - }catch (final SignatureException e) { - throw new BaseApiException(ErrorCode.INVALID_TOKEN); - } - - Object username = claims.getId(); - if (Objects.isNull(username)) { - throw new BaseApiException(ErrorCode.INVALID_TOKEN); - } - TokenModel model = manager.getToken(authHeader,username.toString()); - if (manager.checkToken(model)) { - //如果token验证成功,将对象传递给下一个请求 - request.setAttribute(JwtConstants.CURRENT_TOKEN_CLAIMS, claims); - //如果token验证成功,将token对应的用户id存在request中,便于之后注入 - request.setAttribute(JwtConstants.CURRENT_USER_NAME, model.getUsername()); - String uri = request.getRequestURI(); - // 判断是否为后台接口或财政划转接口 - String adminApi = "/admin/"; - String transactionApi = "/transaction/"; - if (uri.contains(adminApi) || uri.contains(transactionApi)) { - // 判断管理员权限 - boolean hasPermission = userMapper.hasAdminPermission(model.getUsername()); - if (hasPermission) { - return true; - } else { - throw new BaseApiException(ErrorCode.ACCESS_DENIED); - } - } - return true; - } else { - throw new BaseApiException(ErrorCode.TOKEN_); - } - } - - @Override - public void postHandle(HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse, Object obj, ModelAndView modelandview) throws Exception { - // TODO Auto-generated method stub - } - -} diff --git a/src/main/java/com/rymcu/forest/lucene/api/LuceneSearchController.java b/src/main/java/com/rymcu/forest/lucene/api/LuceneSearchController.java index baebd86..2a0837b 100644 --- a/src/main/java/com/rymcu/forest/lucene/api/LuceneSearchController.java +++ b/src/main/java/com/rymcu/forest/lucene/api/LuceneSearchController.java @@ -17,8 +17,10 @@ import com.rymcu.forest.lucene.service.UserLuceneService; import com.rymcu.forest.lucene.util.ArticleIndexUtil; import com.rymcu.forest.lucene.util.PortfolioIndexUtil; import com.rymcu.forest.lucene.util.UserIndexUtil; -import com.rymcu.forest.util.Utils; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import javax.annotation.PostConstruct; import javax.annotation.Resource; diff --git a/src/main/java/com/rymcu/forest/lucene/lucene/PortfolioBeanIndex.java b/src/main/java/com/rymcu/forest/lucene/lucene/PortfolioBeanIndex.java index 12de8cf..544bb74 100644 --- a/src/main/java/com/rymcu/forest/lucene/lucene/PortfolioBeanIndex.java +++ b/src/main/java/com/rymcu/forest/lucene/lucene/PortfolioBeanIndex.java @@ -1,7 +1,6 @@ package com.rymcu.forest.lucene.lucene; import com.rymcu.forest.lucene.model.PortfolioLucene; -import com.rymcu.forest.lucene.model.UserLucene; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.TextField; diff --git a/src/main/java/com/rymcu/forest/lucene/mapper/PortfolioLuceneMapper.java b/src/main/java/com/rymcu/forest/lucene/mapper/PortfolioLuceneMapper.java index b75a7a4..c44ca01 100644 --- a/src/main/java/com/rymcu/forest/lucene/mapper/PortfolioLuceneMapper.java +++ b/src/main/java/com/rymcu/forest/lucene/mapper/PortfolioLuceneMapper.java @@ -1,10 +1,7 @@ package com.rymcu.forest.lucene.mapper; import com.rymcu.forest.dto.PortfolioDTO; -import com.rymcu.forest.dto.UserDTO; -import com.rymcu.forest.entity.Portfolio; import com.rymcu.forest.lucene.model.PortfolioLucene; -import com.rymcu.forest.lucene.model.UserLucene; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; diff --git a/src/main/java/com/rymcu/forest/lucene/model/ArticleLucene.java b/src/main/java/com/rymcu/forest/lucene/model/ArticleLucene.java index 125cb11..e870484 100644 --- a/src/main/java/com/rymcu/forest/lucene/model/ArticleLucene.java +++ b/src/main/java/com/rymcu/forest/lucene/model/ArticleLucene.java @@ -6,8 +6,6 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import javax.persistence.Id; - /** * ArticleLucene * diff --git a/src/main/java/com/rymcu/forest/lucene/model/UserLucene.java b/src/main/java/com/rymcu/forest/lucene/model/UserLucene.java index 708494a..e6d9e91 100644 --- a/src/main/java/com/rymcu/forest/lucene/model/UserLucene.java +++ b/src/main/java/com/rymcu/forest/lucene/model/UserLucene.java @@ -5,10 +5,6 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import org.apache.ibatis.type.JdbcType; -import tk.mybatis.mapper.annotation.ColumnType; - -import javax.persistence.Column; /** * UserLucene diff --git a/src/main/java/com/rymcu/forest/lucene/service/impl/UserLuceneServiceImpl.java b/src/main/java/com/rymcu/forest/lucene/service/impl/UserLuceneServiceImpl.java index 78c8a9f..61212b3 100644 --- a/src/main/java/com/rymcu/forest/lucene/service/impl/UserLuceneServiceImpl.java +++ b/src/main/java/com/rymcu/forest/lucene/service/impl/UserLuceneServiceImpl.java @@ -1,14 +1,14 @@ package com.rymcu.forest.lucene.service.impl; import com.rymcu.forest.dto.UserDTO; -import com.rymcu.forest.lucene.lucene.UserBeanIndex; import com.rymcu.forest.lucene.lucene.IKAnalyzer; +import com.rymcu.forest.lucene.lucene.UserBeanIndex; import com.rymcu.forest.lucene.mapper.UserLuceneMapper; import com.rymcu.forest.lucene.model.UserLucene; import com.rymcu.forest.lucene.service.UserLuceneService; import com.rymcu.forest.lucene.util.LucenePath; -import com.rymcu.forest.lucene.util.UserIndexUtil; import com.rymcu.forest.lucene.util.SearchUtil; +import com.rymcu.forest.lucene.util.UserIndexUtil; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.document.Document; diff --git a/src/main/java/com/rymcu/forest/mapper/UserMapper.java b/src/main/java/com/rymcu/forest/mapper/UserMapper.java index ce13898..d42c22e 100644 --- a/src/main/java/com/rymcu/forest/mapper/UserMapper.java +++ b/src/main/java/com/rymcu/forest/mapper/UserMapper.java @@ -20,7 +20,7 @@ public interface UserMapper extends Mapper { * @param account * @return */ - User findByAccount(@Param("account") String account); + User selectByAccount(@Param("account") String account); /** * 添加用户权限 @@ -147,10 +147,10 @@ public interface UserMapper extends Mapper { /** * 更新用户最后在线时间 - * @param email + * @param account * @return */ - Integer updateLastOnlineTimeByEmail(@Param("email") String email); + Integer updateLastOnlineTimeByAccount(@Param("account") String account); /** * 判断用户是否拥有管理员权限 diff --git a/src/main/java/com/rymcu/forest/service/ArticleThumbsUpService.java b/src/main/java/com/rymcu/forest/service/ArticleThumbsUpService.java index 8c71913..6185201 100644 --- a/src/main/java/com/rymcu/forest/service/ArticleThumbsUpService.java +++ b/src/main/java/com/rymcu/forest/service/ArticleThumbsUpService.java @@ -2,7 +2,6 @@ package com.rymcu.forest.service; import com.rymcu.forest.core.service.Service; import com.rymcu.forest.entity.ArticleThumbsUp; -import com.rymcu.forest.web.api.exception.BaseApiException; /** * @author ronger diff --git a/src/main/java/com/rymcu/forest/service/UserService.java b/src/main/java/com/rymcu/forest/service/UserService.java index 9c74efb..a9e36bf 100644 --- a/src/main/java/com/rymcu/forest/service/UserService.java +++ b/src/main/java/com/rymcu/forest/service/UserService.java @@ -8,7 +8,7 @@ import com.rymcu.forest.entity.UserExtend; import org.apache.ibatis.exceptions.TooManyResultsException; import java.util.List; -import java.util.Map; +import java.util.Set; /** @@ -43,7 +43,7 @@ public interface UserService extends Service { * @param password 密码 * @return Map */ - TokenUser login(String account, String password) throws ServiceException; + TokenUser login(String account, String password); /** * 通过 account 获取用户信息接口 @@ -169,10 +169,10 @@ public interface UserService extends Service { /** * 通过邮箱更新用户最后登录时间 * - * @param email + * @param account * @return */ - Integer updateLastOnlineTimeByEmail(String email); + Integer updateLastOnlineTimeByAccount(String account); /** * 查询用户扩展信息 @@ -181,4 +181,18 @@ public interface UserService extends Service { * @return */ UserExtend findUserExtendInfo(Long idUser); + + /** + * 刷新 token + * @param refreshToken + * @return + */ + TokenUser refreshToken(String refreshToken); + + /** + * 查询用户权限 + * @param user + * @return + */ + Set findUserPermissions(User user); } diff --git a/src/main/java/com/rymcu/forest/service/impl/SponsorServiceImpl.java b/src/main/java/com/rymcu/forest/service/impl/SponsorServiceImpl.java index c9f2216..fce9036 100644 --- a/src/main/java/com/rymcu/forest/service/impl/SponsorServiceImpl.java +++ b/src/main/java/com/rymcu/forest/service/impl/SponsorServiceImpl.java @@ -7,14 +7,12 @@ import com.rymcu.forest.core.service.AbstractService; import com.rymcu.forest.dto.ArticleDTO; import com.rymcu.forest.entity.Sponsor; import com.rymcu.forest.entity.TransactionRecord; -import com.rymcu.forest.entity.User; import com.rymcu.forest.enumerate.TransactionCode; import com.rymcu.forest.enumerate.TransactionEnum; import com.rymcu.forest.mapper.SponsorMapper; import com.rymcu.forest.service.ArticleService; import com.rymcu.forest.service.SponsorService; import com.rymcu.forest.service.TransactionRecordService; -import com.rymcu.forest.util.UserUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/com/rymcu/forest/service/impl/TagServiceImpl.java b/src/main/java/com/rymcu/forest/service/impl/TagServiceImpl.java index ce37474..659fa0f 100644 --- a/src/main/java/com/rymcu/forest/service/impl/TagServiceImpl.java +++ b/src/main/java/com/rymcu/forest/service/impl/TagServiceImpl.java @@ -126,9 +126,6 @@ public class TagServiceImpl extends AbstractService implements TagService { throw new BusinessException("标签 '" + tag.getTagTitle() + "' 已存在!"); } } - tag = new Tag(); - tag.setTagTitle(tag.getTagTitle()); - tag.setTagUri(tag.getTagUri()); if (StringUtils.isNotBlank(tag.getTagIconPath()) && tag.getTagIconPath().contains("base64")) { String tagIconPath = UploadController.uploadBase64File(tag.getTagIconPath(), 2); tag.setTagIconPath(tagIconPath); diff --git a/src/main/java/com/rymcu/forest/service/impl/TopicServiceImpl.java b/src/main/java/com/rymcu/forest/service/impl/TopicServiceImpl.java index d8e0746..c2b434d 100644 --- a/src/main/java/com/rymcu/forest/service/impl/TopicServiceImpl.java +++ b/src/main/java/com/rymcu/forest/service/impl/TopicServiceImpl.java @@ -1,7 +1,5 @@ package com.rymcu.forest.service.impl; -import com.github.pagehelper.PageHelper; -import com.github.pagehelper.PageInfo; import com.rymcu.forest.core.exception.BusinessException; import com.rymcu.forest.core.exception.ServiceException; import com.rymcu.forest.core.service.AbstractService; @@ -60,9 +58,6 @@ public class TopicServiceImpl extends AbstractService implements TopicSer throw new BusinessException("专题 '" + topic.getTopicTitle() + "' 已存在!"); } } - topic = new Topic(); - topic.setTopicTitle(topic.getTopicTitle()); - topic.setTopicUri(topic.getTopicUri()); if (StringUtils.isNotBlank(topic.getTopicIconPath()) && topic.getTopicIconPath().contains("base64")) { String topicIconPath = UploadController.uploadBase64File(topic.getTopicIconPath(), 3); topic.setTopicIconPath(topicIconPath); diff --git a/src/main/java/com/rymcu/forest/service/impl/TransactionRecordServiceImpl.java b/src/main/java/com/rymcu/forest/service/impl/TransactionRecordServiceImpl.java index bc5c5d1..9992e41 100644 --- a/src/main/java/com/rymcu/forest/service/impl/TransactionRecordServiceImpl.java +++ b/src/main/java/com/rymcu/forest/service/impl/TransactionRecordServiceImpl.java @@ -13,7 +13,6 @@ import com.rymcu.forest.mapper.BankAccountMapper; import com.rymcu.forest.mapper.TransactionRecordMapper; import com.rymcu.forest.service.TransactionRecordService; import com.rymcu.forest.util.DateUtil; -import org.apache.commons.lang.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/com/rymcu/forest/service/impl/UserServiceImpl.java b/src/main/java/com/rymcu/forest/service/impl/UserServiceImpl.java index e6855cd..c68c2c0 100644 --- a/src/main/java/com/rymcu/forest/service/impl/UserServiceImpl.java +++ b/src/main/java/com/rymcu/forest/service/impl/UserServiceImpl.java @@ -1,14 +1,14 @@ package com.rymcu.forest.service.impl; +import com.github.f4b6a3.ulid.UlidCreator; +import com.rymcu.forest.auth.JwtConstants; +import com.rymcu.forest.auth.TokenManager; import com.rymcu.forest.core.exception.*; import com.rymcu.forest.core.service.AbstractService; -import com.rymcu.forest.core.service.redis.RedisService; import com.rymcu.forest.dto.*; import com.rymcu.forest.entity.Role; import com.rymcu.forest.entity.User; import com.rymcu.forest.entity.UserExtend; -import com.rymcu.forest.jwt.def.JwtConstants; -import com.rymcu.forest.jwt.service.TokenManager; import com.rymcu.forest.lucene.model.UserLucene; import com.rymcu.forest.lucene.util.UserIndexUtil; import com.rymcu.forest.mapper.RoleMapper; @@ -16,18 +16,20 @@ import com.rymcu.forest.mapper.UserExtendMapper; import com.rymcu.forest.mapper.UserMapper; import com.rymcu.forest.service.LoginRecordService; import com.rymcu.forest.service.UserService; -import com.rymcu.forest.util.BeanCopierUtil; import com.rymcu.forest.util.Utils; import com.rymcu.forest.web.api.common.UploadController; import org.apache.commons.lang.StringUtils; import org.apache.ibatis.exceptions.TooManyResultsException; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UnknownAccountException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.*; +import java.util.concurrent.TimeUnit; /** @@ -40,8 +42,9 @@ public class UserServiceImpl extends AbstractService implements UserServic private UserMapper userMapper; @Resource private RoleMapper roleMapper; - @Resource - private RedisService redisService; + + @Autowired + private StringRedisTemplate redisTemplate; @Resource private TokenManager tokenManager; @Resource @@ -54,16 +57,16 @@ public class UserServiceImpl extends AbstractService implements UserServic @Override public User findByAccount(String account) throws TooManyResultsException { - return userMapper.findByAccount(account); + return userMapper.selectByAccount(account); } @Override @Transactional(rollbackFor = Exception.class) public boolean register(String email, String password, String code) { - String vCode = redisService.get(email); + String vCode = redisTemplate.boundValueOps(email).get(); if (StringUtils.isNotBlank(vCode)) { if (vCode.equals(code)) { - User user = userMapper.findByAccount(email); + User user = userMapper.selectByAccount(email); if (user != null) { throw new AccountExistsException("该邮箱已被注册!"); } else { @@ -77,7 +80,7 @@ public class UserServiceImpl extends AbstractService implements UserServic user.setUpdatedTime(user.getCreatedTime()); user.setAvatarUrl(DEFAULT_AVATAR); userMapper.insertSelective(user); - user = userMapper.findByAccount(email); + user = userMapper.selectByAccount(email); Role role = roleMapper.selectRoleByInputCode("user"); userMapper.insertUserRole(user.getIdUser(), role.getIdRole()); UserIndexUtil.addIndex(UserLucene.builder() @@ -85,7 +88,7 @@ public class UserServiceImpl extends AbstractService implements UserServic .nickname(user.getNickname()) .signature(user.getSignature()) .build()); - redisService.delete(email); + redisTemplate.delete(email); return true; } } @@ -115,17 +118,17 @@ public class UserServiceImpl extends AbstractService implements UserServic @Override public TokenUser login(String account, String password) { - User user = userMapper.findByAccount(account); + User user = userMapper.selectByAccount(account); if (user != null) { if (Utils.comparePwd(password, user.getPassword())) { userMapper.updateLastLoginTime(user.getIdUser()); - userMapper.updateLastOnlineTimeByEmail(user.getEmail()); + userMapper.updateLastOnlineTimeByAccount(user.getAccount()); TokenUser tokenUser = new TokenUser(); - BeanCopierUtil.copy(user, tokenUser); - tokenUser.setToken(tokenManager.createToken(user.getEmail())); - tokenUser.setWeights(userMapper.selectRoleWeightsByUser(user.getIdUser())); + tokenUser.setToken(tokenManager.createToken(user.getAccount())); + tokenUser.setRefreshToken(UlidCreator.getUlid().toString()); + redisTemplate.boundValueOps(tokenUser.getRefreshToken()).set(account, JwtConstants.REFRESH_TOKEN_EXPIRES_HOUR, TimeUnit.HOURS); // 保存登录日志 - loginRecordService.saveLoginRecord(tokenUser.getIdUser()); + loginRecordService.saveLoginRecord(user.getIdUser()); return tokenUser; } else { throw new AuthenticationException("密码错误"); @@ -142,7 +145,7 @@ public class UserServiceImpl extends AbstractService implements UserServic @Override public boolean forgetPassword(String code, String password) throws ServiceException { - String email = redisService.get(code); + String email = redisTemplate.boundValueOps(code).get(); if (StringUtils.isBlank(email)) { throw new ServiceException("链接已失效"); } else { @@ -250,7 +253,7 @@ public class UserServiceImpl extends AbstractService implements UserServic Long idUser = changeEmailDTO.getIdUser(); String email = changeEmailDTO.getEmail(); String code = changeEmailDTO.getCode(); - String vCode = redisService.get(email); + String vCode = redisTemplate.boundValueOps(email).get(); if (StringUtils.isNotBlank(vCode) && StringUtils.isNotBlank(code) && vCode.equals(code)) { int result = userMapper.updateEmail(idUser, email); if (result == 0) { @@ -272,13 +275,13 @@ public class UserServiceImpl extends AbstractService implements UserServic public List findUsers(UserSearchDTO searchDTO) { List users = userMapper.selectUsers(searchDTO); users.forEach(user -> { - user.setOnlineStatus(getUserOnlineStatus(user.getEmail())); + user.setOnlineStatus(getUserOnlineStatus(user.getAccount())); }); return users; } - private Integer getUserOnlineStatus(String email) { - String lastOnlineTime = redisService.get(JwtConstants.LAST_ONLINE + email); + private Integer getUserOnlineStatus(String account) { + String lastOnlineTime = redisTemplate.boundValueOps(JwtConstants.LAST_ONLINE + account).get(); if (StringUtils.isBlank(lastOnlineTime)) { return 0; } @@ -286,8 +289,8 @@ public class UserServiceImpl extends AbstractService implements UserServic } @Override - public Integer updateLastOnlineTimeByEmail(String email) { - return userMapper.updateLastOnlineTimeByEmail(email); + public Integer updateLastOnlineTimeByAccount(String account) { + return userMapper.updateLastOnlineTimeByAccount(account); } @Override @@ -300,4 +303,36 @@ public class UserServiceImpl extends AbstractService implements UserServic } return userExtend; } + + @Override + public TokenUser refreshToken(String refreshToken) { + String account = redisTemplate.boundValueOps(refreshToken).get(); + if (StringUtils.isNotBlank(account)) { + User nucleicUser = userMapper.selectByAccount(account); + if (nucleicUser != null) { + TokenUser tokenUser = new TokenUser(); + tokenUser.setToken(tokenManager.createToken(nucleicUser.getAccount())); + tokenUser.setRefreshToken(UlidCreator.getUlid().toString()); + redisTemplate.boundValueOps(tokenUser.getRefreshToken()).set(account, JwtConstants.REFRESH_TOKEN_EXPIRES_HOUR, TimeUnit.HOURS); + redisTemplate.delete(refreshToken); + return tokenUser; + } else { + throw new UnknownAccountException("未知账号"); + } + } + return null; + } + + @Override + public Set findUserPermissions(User user) { + Set permissions = new HashSet<>(); + List roles = roleMapper.selectRoleByIdUser(user.getIdUser()); + for (Role role : roles) { + if (StringUtils.isNotBlank(role.getInputCode())) { + permissions.add(role.getInputCode()); + } + } + permissions.add("user"); + return permissions; + } } diff --git a/src/main/java/com/rymcu/forest/task/BaiDuCronTask.java b/src/main/java/com/rymcu/forest/task/BaiDuCronTask.java index 6872773..9c43f2d 100644 --- a/src/main/java/com/rymcu/forest/task/BaiDuCronTask.java +++ b/src/main/java/com/rymcu/forest/task/BaiDuCronTask.java @@ -5,7 +5,6 @@ import com.rymcu.forest.util.BaiDuUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; /** * @author ronger diff --git a/src/main/java/com/rymcu/forest/util/UserUtils.java b/src/main/java/com/rymcu/forest/util/UserUtils.java index c69b70e..9655bcf 100644 --- a/src/main/java/com/rymcu/forest/util/UserUtils.java +++ b/src/main/java/com/rymcu/forest/util/UserUtils.java @@ -1,17 +1,16 @@ package com.rymcu.forest.util; +import com.rymcu.forest.auth.JwtConstants; +import com.rymcu.forest.auth.TokenManager; +import com.rymcu.forest.auth.TokenModel; import com.rymcu.forest.dto.TokenUser; import com.rymcu.forest.entity.User; -import com.rymcu.forest.jwt.def.JwtConstants; -import com.rymcu.forest.jwt.model.TokenModel; -import com.rymcu.forest.jwt.service.TokenManager; import com.rymcu.forest.mapper.UserMapper; -import com.rymcu.forest.web.api.exception.BaseApiException; -import com.rymcu.forest.web.api.exception.ErrorCode; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureException; import org.apache.commons.lang.StringUtils; +import org.apache.shiro.authz.UnauthorizedException; import java.util.Objects; @@ -28,7 +27,7 @@ public class UserUtils { * * @return */ - public static User getCurrentUserByToken() throws BaseApiException { + public static User getCurrentUserByToken() { String authHeader = ContextHolderUtils.getRequest().getHeader(JwtConstants.AUTHORIZATION); if (authHeader == null) { return null; @@ -38,16 +37,16 @@ public class UserUtils { try { claims = Jwts.parser().setSigningKey(JwtConstants.JWT_SECRET).parseClaimsJws(authHeader).getBody(); } catch (final SignatureException e) { - throw new BaseApiException(ErrorCode.UNAUTHORIZED); + throw new UnauthorizedException(); } Object account = claims.getId(); if (StringUtils.isNotBlank(Objects.toString(account, ""))) { TokenModel model = tokenManager.getToken(authHeader, account.toString()); if (tokenManager.checkToken(model)) { - return userMapper.findByAccount(account.toString()); + return userMapper.selectByAccount(account.toString()); } } else { - throw new BaseApiException(ErrorCode.UNAUTHORIZED); + throw new UnauthorizedException(); } return null; } @@ -65,13 +64,12 @@ public class UserUtils { if (StringUtils.isNotBlank(Objects.toString(account, ""))) { TokenModel model = tokenManager.getToken(token, account.toString()); if (tokenManager.checkToken(model)) { - User user = userMapper.findByAccount(account.toString()); + User user = userMapper.selectByAccount(account.toString()); if (user != null) { TokenUser tokenUser = new TokenUser(); BeanCopierUtil.copy(user, tokenUser); tokenUser.setAccount(user.getEmail()); tokenUser.setToken(token); - tokenUser.setWeights(userMapper.selectRoleWeightsByUser(user.getIdUser())); return tokenUser; } } diff --git a/src/main/java/com/rymcu/forest/web/api/admin/AdminArticleController.java b/src/main/java/com/rymcu/forest/web/api/admin/AdminArticleController.java index 9b2cac8..c94b02b 100644 --- a/src/main/java/com/rymcu/forest/web/api/admin/AdminArticleController.java +++ b/src/main/java/com/rymcu/forest/web/api/admin/AdminArticleController.java @@ -10,7 +10,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; -import java.util.Map; /** * Created on 2022/1/3 10:11. diff --git a/src/main/java/com/rymcu/forest/web/api/admin/AdminCurrencyRuleController.java b/src/main/java/com/rymcu/forest/web/api/admin/AdminCurrencyRuleController.java index 39cf31a..b59d2d3 100644 --- a/src/main/java/com/rymcu/forest/web/api/admin/AdminCurrencyRuleController.java +++ b/src/main/java/com/rymcu/forest/web/api/admin/AdminCurrencyRuleController.java @@ -7,7 +7,6 @@ import com.rymcu.forest.core.result.GlobalResultGenerator; import com.rymcu.forest.dto.TransactionRecordDTO; import com.rymcu.forest.entity.CurrencyRule; import com.rymcu.forest.service.CurrencyRuleService; -import com.rymcu.forest.util.Utils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -15,9 +14,7 @@ import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** * Created on 2022/3/6 18:26. diff --git a/src/main/java/com/rymcu/forest/web/api/admin/DashboardController.java b/src/main/java/com/rymcu/forest/web/api/admin/DashboardController.java index 6b17356..530aec7 100644 --- a/src/main/java/com/rymcu/forest/web/api/admin/DashboardController.java +++ b/src/main/java/com/rymcu/forest/web/api/admin/DashboardController.java @@ -9,14 +9,12 @@ import com.rymcu.forest.dto.BankAccountDTO; import com.rymcu.forest.dto.UserInfoDTO; import com.rymcu.forest.dto.admin.Dashboard; import com.rymcu.forest.service.DashboardService; -import com.rymcu.forest.util.Utils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; -import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/src/main/java/com/rymcu/forest/web/api/auth/AuthController.java b/src/main/java/com/rymcu/forest/web/api/auth/AuthController.java new file mode 100644 index 0000000..40bc1ad --- /dev/null +++ b/src/main/java/com/rymcu/forest/web/api/auth/AuthController.java @@ -0,0 +1,61 @@ +package com.rymcu.forest.web.api.auth; + +import com.alibaba.fastjson2.JSONObject; +import com.rymcu.forest.auth.TokenManager; +import com.rymcu.forest.core.result.GlobalResult; +import com.rymcu.forest.core.result.GlobalResultGenerator; +import com.rymcu.forest.dto.TokenUser; +import com.rymcu.forest.entity.User; +import com.rymcu.forest.service.UserService; +import com.rymcu.forest.util.BeanCopierUtil; +import com.rymcu.forest.util.UserUtils; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.Objects; + +/** + * @author ronger + */ +@RestController +@RequestMapping("/api/v1/auth") +public class AuthController { + + @Resource + private UserService userService; + @Resource + TokenManager tokenManager; + + @PostMapping("/login") + public GlobalResult login(@RequestBody User user) { + TokenUser tokenUser = userService.login(user.getAccount(), user.getPassword()); + return GlobalResultGenerator.genSuccessResult(tokenUser); + } + + @PostMapping("/refresh-token") + public GlobalResult refreshToken(@RequestBody TokenUser tokenUser) { + tokenUser = userService.refreshToken(tokenUser.getRefreshToken()); + return GlobalResultGenerator.genSuccessResult(tokenUser); + } + + @PostMapping("/logout") + public GlobalResult logout() { + User user = UserUtils.getCurrentUserByToken(); + if (Objects.nonNull(user)) { + tokenManager.deleteToken(user.getAccount()); + } + return GlobalResultGenerator.genSuccessResult(); + } + + @GetMapping("/user") + public GlobalResult user() { + User user = UserUtils.getCurrentUserByToken(); + TokenUser tokenUser = new TokenUser(); + BeanCopierUtil.copy(user, tokenUser); + tokenUser.setScope(userService.findUserPermissions(user)); + JSONObject object = new JSONObject(); + object.put("user", tokenUser); + return GlobalResultGenerator.genSuccessResult(object); + } + +} diff --git a/src/main/java/com/rymcu/forest/web/api/bank/BankAccountController.java b/src/main/java/com/rymcu/forest/web/api/bank/BankAccountController.java index 2e1db18..50062f9 100644 --- a/src/main/java/com/rymcu/forest/web/api/bank/BankAccountController.java +++ b/src/main/java/com/rymcu/forest/web/api/bank/BankAccountController.java @@ -8,14 +8,11 @@ import com.rymcu.forest.dto.BankAccountDTO; import com.rymcu.forest.dto.BankAccountSearchDTO; import com.rymcu.forest.dto.TransactionRecordDTO; import com.rymcu.forest.service.BankAccountService; -import com.rymcu.forest.util.Utils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** * @author ronger diff --git a/src/main/java/com/rymcu/forest/web/api/bank/BankController.java b/src/main/java/com/rymcu/forest/web/api/bank/BankController.java index e157a08..d2cf8e0 100644 --- a/src/main/java/com/rymcu/forest/web/api/bank/BankController.java +++ b/src/main/java/com/rymcu/forest/web/api/bank/BankController.java @@ -4,20 +4,15 @@ import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.rymcu.forest.core.result.GlobalResult; import com.rymcu.forest.core.result.GlobalResultGenerator; -import com.rymcu.forest.dto.ArticleDTO; import com.rymcu.forest.dto.BankDTO; -import com.rymcu.forest.entity.Bank; import com.rymcu.forest.service.BankService; -import com.rymcu.forest.util.Utils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** * @author ronger diff --git a/src/main/java/com/rymcu/forest/web/api/bank/WalletController.java b/src/main/java/com/rymcu/forest/web/api/bank/WalletController.java index e9ea1ac..0f2994c 100644 --- a/src/main/java/com/rymcu/forest/web/api/bank/WalletController.java +++ b/src/main/java/com/rymcu/forest/web/api/bank/WalletController.java @@ -7,15 +7,15 @@ import com.rymcu.forest.core.result.GlobalResultGenerator; import com.rymcu.forest.core.service.security.annotation.SecurityInterceptor; import com.rymcu.forest.dto.BankAccountDTO; import com.rymcu.forest.dto.TransactionRecordDTO; +import com.rymcu.forest.entity.User; import com.rymcu.forest.service.BankAccountService; -import com.rymcu.forest.util.Utils; +import com.rymcu.forest.util.UserUtils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; -import java.util.HashMap; import java.util.List; -import java.util.Map; +import java.util.Objects; /** * Created on 2021/12/10 19:25. @@ -31,20 +31,22 @@ public class WalletController { private BankAccountService bankAccountService; - @GetMapping("/{idUser}") - @SecurityInterceptor - public GlobalResult detail(@PathVariable Long idUser) { - BankAccountDTO bankAccount = bankAccountService.findBankAccountByIdUser(idUser); + @GetMapping("/detail") + public GlobalResult detail() { + User user = UserUtils.getCurrentUserByToken(); + BankAccountDTO bankAccount = bankAccountService.findBankAccountByIdUser(user.getIdUser()); return GlobalResultGenerator.genSuccessResult(bankAccount); } @GetMapping("/transaction-records") - @SecurityInterceptor public GlobalResult> transactionRecords(@RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "20") Integer rows, HttpServletRequest request) { - String idUser = request.getParameter("idUser"); + User user = UserUtils.getCurrentUserByToken(); String startDate = request.getParameter("startDate"); String endDate = request.getParameter("endDate"); - BankAccountDTO bankAccount = bankAccountService.findBankAccountByIdUser(Long.valueOf(idUser)); + BankAccountDTO bankAccount = bankAccountService.findBankAccountByIdUser(user.getIdUser()); + if (Objects.isNull(bankAccount)) { + return GlobalResultGenerator.genSuccessResult(new PageInfo<>()); + } PageHelper.startPage(page, rows); List list = bankAccountService.findUserTransactionRecords(bankAccount.getBankAccount(), startDate, endDate); PageInfo pageInfo = new PageInfo(list); diff --git a/src/main/java/com/rymcu/forest/web/api/common/UploadController.java b/src/main/java/com/rymcu/forest/web/api/common/UploadController.java index 788eb28..b397821 100644 --- a/src/main/java/com/rymcu/forest/web/api/common/UploadController.java +++ b/src/main/java/com/rymcu/forest/web/api/common/UploadController.java @@ -1,10 +1,10 @@ package com.rymcu.forest.web.api.common; +import com.rymcu.forest.auth.JwtConstants; import com.rymcu.forest.core.result.GlobalResult; import com.rymcu.forest.core.result.GlobalResultGenerator; import com.rymcu.forest.dto.LinkToImageUrlDTO; import com.rymcu.forest.dto.TokenUser; -import com.rymcu.forest.jwt.def.JwtConstants; import com.rymcu.forest.service.ForestFileService; import com.rymcu.forest.util.FileUtils; import com.rymcu.forest.util.SpringContextHolder; diff --git a/src/main/java/com/rymcu/forest/web/api/notification/NotificationController.java b/src/main/java/com/rymcu/forest/web/api/notification/NotificationController.java index 4508ecc..d4333f4 100644 --- a/src/main/java/com/rymcu/forest/web/api/notification/NotificationController.java +++ b/src/main/java/com/rymcu/forest/web/api/notification/NotificationController.java @@ -9,14 +9,12 @@ import com.rymcu.forest.entity.Notification; import com.rymcu.forest.entity.User; import com.rymcu.forest.service.NotificationService; import com.rymcu.forest.util.UserUtils; -import com.rymcu.forest.util.Utils; import com.rymcu.forest.web.api.exception.BaseApiException; import com.rymcu.forest.web.api.exception.ErrorCode; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.List; -import java.util.Map; import java.util.Objects; /** diff --git a/src/main/java/com/rymcu/forest/web/api/portfolio/PortfolioController.java b/src/main/java/com/rymcu/forest/web/api/portfolio/PortfolioController.java index 81a8e99..2c5b208 100644 --- a/src/main/java/com/rymcu/forest/web/api/portfolio/PortfolioController.java +++ b/src/main/java/com/rymcu/forest/web/api/portfolio/PortfolioController.java @@ -17,8 +17,6 @@ import com.rymcu.forest.web.api.exception.BaseApiException; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; -import java.util.HashMap; -import java.util.Map; /** * @author ronger diff --git a/src/main/java/com/rymcu/forest/web/api/tag/TagController.java b/src/main/java/com/rymcu/forest/web/api/tag/TagController.java index 9b4556a..32260fe 100644 --- a/src/main/java/com/rymcu/forest/web/api/tag/TagController.java +++ b/src/main/java/com/rymcu/forest/web/api/tag/TagController.java @@ -8,12 +8,10 @@ import com.rymcu.forest.dto.ArticleDTO; import com.rymcu.forest.dto.LabelModel; import com.rymcu.forest.service.ArticleService; import com.rymcu.forest.service.TagService; -import com.rymcu.forest.util.Utils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.List; -import java.util.Map; /** * @author ronger diff --git a/src/main/java/com/rymcu/forest/web/api/topic/TopicController.java b/src/main/java/com/rymcu/forest/web/api/topic/TopicController.java index d90d1f9..41365b9 100644 --- a/src/main/java/com/rymcu/forest/web/api/topic/TopicController.java +++ b/src/main/java/com/rymcu/forest/web/api/topic/TopicController.java @@ -9,12 +9,10 @@ import com.rymcu.forest.dto.ArticleDTO; import com.rymcu.forest.entity.Topic; import com.rymcu.forest.service.ArticleService; import com.rymcu.forest.service.TopicService; -import com.rymcu.forest.util.Utils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.List; -import java.util.Map; /** * @author ronger diff --git a/src/main/java/com/rymcu/forest/web/api/user/UserController.java b/src/main/java/com/rymcu/forest/web/api/user/UserController.java index 7e7d06f..01756f1 100644 --- a/src/main/java/com/rymcu/forest/web/api/user/UserController.java +++ b/src/main/java/com/rymcu/forest/web/api/user/UserController.java @@ -13,13 +13,10 @@ import com.rymcu.forest.service.ArticleService; import com.rymcu.forest.service.FollowService; import com.rymcu.forest.service.PortfolioService; import com.rymcu.forest.service.UserService; -import com.rymcu.forest.util.Utils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** * @author ronger diff --git a/src/main/java/com/rymcu/forest/web/api/user/UserInfoController.java b/src/main/java/com/rymcu/forest/web/api/user/UserInfoController.java index 46db78f..70603b1 100644 --- a/src/main/java/com/rymcu/forest/web/api/user/UserInfoController.java +++ b/src/main/java/com/rymcu/forest/web/api/user/UserInfoController.java @@ -13,13 +13,10 @@ import com.rymcu.forest.entity.LoginRecord; import com.rymcu.forest.entity.UserExtend; import com.rymcu.forest.service.LoginRecordService; import com.rymcu.forest.service.UserService; -import com.rymcu.forest.util.Utils; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** * @author ronger diff --git a/src/main/java/mapper/SearchMapper.xml b/src/main/java/mapper/SearchMapper.xml deleted file mode 100644 index 7bdfcb4..0000000 --- a/src/main/java/mapper/SearchMapper.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/java/mapper/UserMapper.xml b/src/main/java/mapper/UserMapper.xml index f2c6cef..c666d3f 100644 --- a/src/main/java/mapper/UserMapper.xml +++ b/src/main/java/mapper/UserMapper.xml @@ -78,11 +78,11 @@ update forest_user set password = #{password} where id = #{idUser} - - update forest_user set last_online_time = sysdate() where email = #{email} + + update forest_user set last_online_time = sysdate() where account = #{account} - select id, nickname, account, password, status, avatar_type, avatar_url, email from forest_user where (account = #{account} or email = #{account} ) and status = 0