Merge pull request #109 from ronger-x/master

🎨 开放数据接口跳过鉴权
This commit is contained in:
ronger 2022-10-28 22:46:27 +08:00 committed by GitHub
commit a86020b95c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 590 additions and 839 deletions

View File

@ -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 历史

14
pom.xml
View File

@ -128,7 +128,7 @@
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.15</version>
<version>2.0.16</version>
</dependency>
<!-- shiro权限控制框架 -->
<dependency>
@ -147,6 +147,12 @@
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>com.github.f4b6a3</groupId>
<artifactId>ulid-creator</artifactId>
<version>5.1.0</version>
</dependency>
<!--apache相关依赖-->
<dependency>
<groupId>commons-lang</groupId>
@ -177,7 +183,7 @@
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.13-SNSAPSHOT</version>
<version>1.2.14</version>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
@ -255,12 +261,12 @@
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.8</version>
<version>5.8.9</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
<version>5.8.8</version>
<version>5.8.9</version>
</dependency>
<dependency>

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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请求会被直接拦截用户看不到任何东西
* 所以我们在这里返回trueController中可以通过 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());
}
}
}

View File

@ -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<Role> 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());
}
}

View File

@ -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();

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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<String> 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);
}
}
}
}

View File

@ -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<Role> roles = roleService.selectRoleByUser(user);
for (Role role : roles) {
if (StringUtils.isNotBlank(role.getInputCode())) {
authorizationInfo.addRole(role.getInputCode());
}
}
List<Permission> 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<String, Object> 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();
}
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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<String, Filter> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("jwt", new JwtFilter());
shiroFilterFactoryBean.setFilters(linkedHashMap);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
//注意过滤器配置顺序 不能颠倒
//配置退出 过滤器,其中的具体的退出代码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<String, Filter> 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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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/**");
}
/**
* 访问静态资源
*/

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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<String> scope;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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());
}
}

View File

@ -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
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -6,8 +6,6 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Id;
/**
* ArticleLucene
*

View File

@ -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

View File

@ -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;

View File

@ -20,7 +20,7 @@ public interface UserMapper extends Mapper<User> {
* @param account
* @return
*/
User findByAccount(@Param("account") String account);
User selectByAccount(@Param("account") String account);
/**
* 添加用户权限
@ -147,10 +147,10 @@ public interface UserMapper extends Mapper<User> {
/**
* 更新用户最后在线时间
* @param email
* @param account
* @return
*/
Integer updateLastOnlineTimeByEmail(@Param("email") String email);
Integer updateLastOnlineTimeByAccount(@Param("account") String account);
/**
* 判断用户是否拥有管理员权限

View File

@ -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

View File

@ -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<User> {
* @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<User> {
/**
* 通过邮箱更新用户最后登录时间
*
* @param email
* @param account
* @return
*/
Integer updateLastOnlineTimeByEmail(String email);
Integer updateLastOnlineTimeByAccount(String account);
/**
* 查询用户扩展信息
@ -181,4 +181,18 @@ public interface UserService extends Service<User> {
* @return
*/
UserExtend findUserExtendInfo(Long idUser);
/**
* 刷新 token
* @param refreshToken
* @return
*/
TokenUser refreshToken(String refreshToken);
/**
* 查询用户权限
* @param user
* @return
*/
Set<String> findUserPermissions(User user);
}

View File

@ -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;

View File

@ -126,9 +126,6 @@ public class TagServiceImpl extends AbstractService<Tag> 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);

View File

@ -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<Topic> 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);

View File

@ -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;

View File

@ -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<User> 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<User> 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<User> 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<User> 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<User> 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<User> 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<User> 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<User> implements UserServic
public List<UserInfoDTO> findUsers(UserSearchDTO searchDTO) {
List<UserInfoDTO> 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<User> 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<User> 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<String> findUserPermissions(User user) {
Set<String> permissions = new HashSet<>();
List<Role> roles = roleMapper.selectRoleByIdUser(user.getIdUser());
for (Role role : roles) {
if (StringUtils.isNotBlank(role.getInputCode())) {
permissions.add(role.getInputCode());
}
}
permissions.add("user");
return permissions;
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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.

View File

@ -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.

View File

@ -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;

View File

@ -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<TokenUser> login(@RequestBody User user) {
TokenUser tokenUser = userService.login(user.getAccount(), user.getPassword());
return GlobalResultGenerator.genSuccessResult(tokenUser);
}
@PostMapping("/refresh-token")
public GlobalResult<TokenUser> 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<JSONObject> 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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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<BankAccountDTO> detail(@PathVariable Long idUser) {
BankAccountDTO bankAccount = bankAccountService.findBankAccountByIdUser(idUser);
@GetMapping("/detail")
public GlobalResult<BankAccountDTO> detail() {
User user = UserUtils.getCurrentUserByToken();
BankAccountDTO bankAccount = bankAccountService.findBankAccountByIdUser(user.getIdUser());
return GlobalResultGenerator.genSuccessResult(bankAccount);
}
@GetMapping("/transaction-records")
@SecurityInterceptor
public GlobalResult<PageInfo<TransactionRecordDTO>> 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<TransactionRecordDTO> list = bankAccountService.findUserTransactionRecords(bankAccount.getBankAccount(), startDate, endDate);
PageInfo<TransactionRecordDTO> pageInfo = new PageInfo(list);

View File

@ -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;

View File

@ -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;
/**

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.rymcu.forest.mapper.SearchMapper">
<resultMap id="BaseResultMap" type="com.rymcu.forest.dto.SearchModel">
<result column="label" property="label"></result>
<result column="value" property="value"></result>
<result column="type" property="type"></result>
</resultMap>
<select id="searchInitialArticleSearch" resultMap="BaseResultMap">
select article_title as label, id as value, 'article' as type from forest_article where article_status = 0
</select>
<select id="searchInitialPortfolioSearch" resultMap="BaseResultMap">
select portfolio_title as label, id as value, 'portfolio' as type from forest_portfolio
</select>
<select id="searchInitialUserSearch" resultMap="BaseResultMap">
select nickname as label, account as value, 'user' as type from forest_user where status = 0
</select>
</mapper>

View File

@ -78,11 +78,11 @@
<update id="updatePasswordById">
update forest_user set password = #{password} where id = #{idUser}
</update>
<update id="updateLastOnlineTimeByEmail">
update forest_user set last_online_time = sysdate() where email = #{email}
<update id="updateLastOnlineTimeByAccount">
update forest_user set last_online_time = sysdate() where account = #{account}
</update>
<select id="findByAccount" resultMap="BaseResultMap">
<select id="selectByAccount" resultMap="BaseResultMap">
select id, nickname, account, password, status, avatar_type, avatar_url, email from forest_user where (account = #{account} or email = #{account} ) and status = 0
</select>
<select id="findUserInfoByAccount" resultMap="UserInfoResultMapper">

View File

@ -0,0 +1,100 @@
publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANbY1W9TXU+OdseRBVw5/eq1rs5UVQZie5ydEPYIurRzF4oBORu/+Sjqt+c5mHulleuiD4Yov+0e/CXODg4FYu8CAwEAAQ==
spring:
thymeleaf:
prefix: classpath:/templates/
suffix: .html
mode: HTML
encoding: UTF-8
servlet:
content-type: text/html
cache: false
redis:
host: 192.168.31.200
port: 6379
password: d9d2j9w2
database: 1
timeout: 3000
jedis:
pool:
max-active: 8
max-wait: 1
max-idle: 500
min-idle: 0
datasource:
druid:
url: jdbc:mysql://192.168.31.200:3306/forest?characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true
username: root
password: Ldh1NrsXJntz5nTXeO0nPh+8s1FMe+JHCfXy2Zj3egqqjegN+mNe+53ZmduUlVdE4ZqKuUho7T9yftc6AYYKog==
driver-class-name: com.mysql.cj.jdbc.Driver
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
time-between-eviction-runs-millis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
min-evictable-idle-time-millis: 30000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
connection-properties: config.decrypt=true;config.decrypt.key=${publickey}
filters: config,stat
max-pool-prepared-statement-per-connection-size: 100
stat-view-servlet:
enabled: true
allow:
login-username: admin
login-password: rymcu.Test1
filter:
stat:
log-slow-sql: true
mail:
host: smtp.ym.163.com
port: 465
username: service@rymcu.com
password: 4W3tCXdyk0Gm
wx:
open:
componentAppId: wx9c4a7dfb3238d5f6
componentSecret: e32a6f75ab6b746ec3ae38a39a79ba22
componentToken: rymcu
componentAesKey: NWIwMDQyZjU0YWI2NGFlZThkOWZhZTg3NTg4NzQwN2E
mp:
configs:
- appId: wxf085386aa07c0857
secret: aabd075d2851764714fd14a0d0b1b8b4
token: rymcu
aesKey: lvn3mMSnFOvbnIJVNhHQqjWb9swe66L1xIcerJSs0fm
- appId: wxa49093339a5a822b
secret: 29e9390e6d58d57a2b2a2350dbee8754
token: qwert
aesKey:
miniapp:
configs:
- appid: wxb4fff78a6b878cf7
secret: c8735d0ccc8497b8509dc2762246cb37
token: #微信小程序消息服务器配置的token
aesKey: #微信小程序消息服务器配置的EncodingAESKey
msgDataFormat: JSON
env: dev
logging:
file:
path: /logs/forest
level:
com:
rymcu: info
server:
port: 8099
servlet:
context-path: /forest
max-http-header-size: 1048576
version: 1.0
resource:
domain: http://test.rymcu.com
file-path: http://test.rymcu.com
pic-path: /opt/nebula/static
baidu:
data:
site: https://rymcu.com
token: 9cdKR6bVCJzxDEJS
ai:
appId: 22891829
appKey: HKxdO8ioaUmltZh0eaOVMsmW
secretKey: GXOtl3XtiIkVA3CPsc3c29Pqa4V290Yr