🔒 修复了一些已知的安全问题
🔒 修复了一些已知的安全问题
This commit is contained in:
commit
8e42504033
@ -21,7 +21,7 @@ import java.util.Map;
|
||||
@RequestMapping("/api/v1/answer")
|
||||
public class AnswerController {
|
||||
|
||||
private final static String ANSWER_API_URL = "http://101.132.237.86:8089/question";
|
||||
private final static String ANSWER_API_URL = "http://1.116.175.112:8089/question";
|
||||
|
||||
@GetMapping("/today")
|
||||
public GlobalResult today() throws BaseApiException {
|
||||
|
@ -0,0 +1,54 @@
|
||||
package com.rymcu.forest.config;
|
||||
|
||||
import com.rymcu.forest.entity.User;
|
||||
import com.rymcu.forest.jwt.def.JwtConstants;
|
||||
import com.rymcu.forest.service.UserService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
|
||||
/**
|
||||
* Created on 2021/10/9 9:25.
|
||||
*
|
||||
* @author ronger
|
||||
* @email ronger-x@outlook.com
|
||||
* @packageName com.rymcu.forest.config
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
|
||||
|
||||
@Resource
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
|
||||
super(listenerContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 针对 redis 数据失效事件,进行数据处理
|
||||
*
|
||||
* @param message key
|
||||
* @param pattern pattern
|
||||
*/
|
||||
@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, "");
|
||||
log.info("拿到过期的数据:{}", expiredKey);
|
||||
log.info("处理后的数据:{}", email);
|
||||
userService.updateLastOnlineTimeByEmail(email);
|
||||
}
|
||||
super.onMessage(message, pattern);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.rymcu.forest.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
|
||||
/**
|
||||
* Created on 2021/10/9 9:23.
|
||||
*
|
||||
* @author ronger
|
||||
* @email ronger-x@outlook.com
|
||||
* @packageName com.rymcu.forest.config
|
||||
*/
|
||||
@Configuration
|
||||
public class RedisListenerConfig {
|
||||
|
||||
@Bean
|
||||
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
|
||||
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
|
||||
container.setConnectionFactory(connectionFactory);
|
||||
return container;
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package com.rymcu.forest.dto;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@ -37,4 +38,12 @@ public class UserInfoDTO implements Serializable {
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm")
|
||||
private Date lastLoginTime;
|
||||
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm")
|
||||
private Date lastOnlineTime;
|
||||
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createdTime;
|
||||
|
||||
private Integer onlineStatus;
|
||||
|
||||
}
|
||||
|
@ -108,9 +108,16 @@ public class User implements Serializable,Cloneable {
|
||||
private Date createdTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
* 更新时间
|
||||
* */
|
||||
@Column(name = "updated_time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updatedTime;
|
||||
|
||||
/**
|
||||
* 最后在线时间
|
||||
* */
|
||||
@Column(name = "last_online_time")
|
||||
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date lastOnlineTime;
|
||||
}
|
@ -4,6 +4,7 @@ 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;
|
||||
@ -14,6 +15,7 @@ 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;
|
||||
@ -28,6 +30,8 @@ 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 {
|
||||
@ -36,7 +40,6 @@ public class RestAuthTokenInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception {
|
||||
|
||||
//从header中得到token
|
||||
String authHeader = request.getHeader(JwtConstants.AUTHORIZATION);
|
||||
if(StringUtils.isBlank(authHeader)){
|
||||
@ -63,6 +66,19 @@ public class RestAuthTokenInterceptor implements HandlerInterceptor {
|
||||
request.setAttribute(JwtConstants.CURRENT_TOKEN_CLAIMS, claims);
|
||||
//如果token验证成功,将token对应的用户id存在request中,便于之后注入
|
||||
request.setAttribute(JwtConstants.CURRENT_USER_NAME, model.getUsername());
|
||||
// 判断是否为后台接口或财政划转接口
|
||||
String adminApi = "/admin";
|
||||
String transactionApi = "/transaction";
|
||||
String uri = request.getRequestURI();
|
||||
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_);
|
||||
|
@ -14,6 +14,8 @@ public class JwtConstants {
|
||||
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 LAST_ONLINE_EXPIRES_MINUTE = 10;
|
||||
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -50,6 +52,9 @@ public class RedisTokenManager implements TokenManager {
|
||||
}
|
||||
//如果验证成功,说明此用户进行了一次有效操作,延长token的过期时间
|
||||
redisTemplate.boundValueOps(model.getUsername()).expire(JwtConstants.TOKEN_EXPIRES_HOUR, TimeUnit.HOURS);
|
||||
StringBuilder key = new StringBuilder();
|
||||
key.append(JwtConstants.LAST_ONLINE).append(model.getUsername());
|
||||
redisTemplate.boundValueOps(key.toString()).set(LocalDateTime.now().toString(), JwtConstants.LAST_ONLINE_EXPIRES_MINUTE, TimeUnit.MINUTES);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -95,13 +95,11 @@ public interface UserMapper extends Mapper<User> {
|
||||
* @param nickname
|
||||
* @param avatarType
|
||||
* @param avatarUrl
|
||||
* @param email
|
||||
* @param phone
|
||||
* @param signature
|
||||
* @param sex
|
||||
* @return
|
||||
*/
|
||||
Integer updateUserInfo(@Param("idUser") Integer idUser, @Param("nickname") String nickname, @Param("avatarType") String avatarType, @Param("avatarUrl") String avatarUrl, @Param("email") String email, @Param("phone") String phone, @Param("signature") String signature, @Param("sex") String sex);
|
||||
Integer updateUserInfo(@Param("idUser") Integer idUser, @Param("nickname") String nickname, @Param("avatarType") String avatarType, @Param("avatarUrl") String avatarUrl, @Param("signature") String signature, @Param("sex") String sex);
|
||||
|
||||
/**
|
||||
* 验证昵称是否重复
|
||||
@ -145,5 +143,19 @@ public interface UserMapper extends Mapper<User> {
|
||||
* @param searchDTO
|
||||
* @return
|
||||
*/
|
||||
List<User> selectUsers(@Param("searchDTO") UserSearchDTO searchDTO);
|
||||
List<UserInfoDTO> selectUsers(@Param("searchDTO") UserSearchDTO searchDTO);
|
||||
|
||||
/**
|
||||
* 更新用户最后在线时间
|
||||
* @param email
|
||||
* @return
|
||||
*/
|
||||
Integer updateLastOnlineTimeByEmail(@Param("email") String email);
|
||||
|
||||
/**
|
||||
* 判断用户是否拥有管理员权限
|
||||
* @param email
|
||||
* @return
|
||||
*/
|
||||
boolean hasAdminPermission(@Param("email") String email);
|
||||
}
|
@ -142,5 +142,12 @@ public interface UserService extends Service<User> {
|
||||
* @param searchDTO
|
||||
* @return
|
||||
*/
|
||||
List<User> findUsers(UserSearchDTO searchDTO);
|
||||
List<UserInfoDTO> findUsers(UserSearchDTO searchDTO);
|
||||
|
||||
/**
|
||||
* 通过邮箱查询用户信息
|
||||
* @param email
|
||||
* @return
|
||||
*/
|
||||
Integer updateLastOnlineTimeByEmail(String email);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ 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;
|
||||
@ -198,8 +199,7 @@ public class UserServiceImpl extends AbstractService<User> implements UserServic
|
||||
user.setAvatarUrl(avatarUrl);
|
||||
user.setAvatarType("0");
|
||||
}
|
||||
Integer result = userMapper.updateUserInfo(user.getIdUser(), user.getNickname(), user.getAvatarType(),user.getAvatarUrl(),
|
||||
user.getEmail(),user.getPhone(),user.getSignature(), user.getSex());
|
||||
Integer result = userMapper.updateUserInfo(user.getIdUser(), user.getNickname(), user.getAvatarType(),user.getAvatarUrl(),user.getSignature(), user.getSex());
|
||||
UserIndexUtil.addIndex(UserLucene.builder()
|
||||
.idUser(user.getIdUser())
|
||||
.nickname(user.getNickname())
|
||||
@ -262,7 +262,7 @@ public class UserServiceImpl extends AbstractService<User> implements UserServic
|
||||
String email = changeEmailDTO.getEmail();
|
||||
String code = changeEmailDTO.getCode();
|
||||
String vCode = redisService.get(email);
|
||||
if(StringUtils.isNotBlank(vCode)){
|
||||
if(StringUtils.isNotBlank(vCode) && StringUtils.isNotBlank(code)){
|
||||
if(vCode.equals(code)){
|
||||
userMapper.updateEmail(idUser, email);
|
||||
map.put("message","更新成功!");
|
||||
@ -282,7 +282,24 @@ public class UserServiceImpl extends AbstractService<User> implements UserServic
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<User> findUsers(UserSearchDTO searchDTO) {
|
||||
return userMapper.selectUsers(searchDTO);
|
||||
public List<UserInfoDTO> findUsers(UserSearchDTO searchDTO) {
|
||||
List<UserInfoDTO> users = userMapper.selectUsers(searchDTO);
|
||||
users.forEach(user -> {
|
||||
user.setOnlineStatus(getUserOnlineStatus(user.getEmail()));
|
||||
});
|
||||
return users;
|
||||
}
|
||||
|
||||
private Integer getUserOnlineStatus(String email) {
|
||||
String lastOnlineTime = redisService.get(JwtConstants.LAST_ONLINE + email);
|
||||
if (StringUtils.isBlank(lastOnlineTime)) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer updateLastOnlineTimeByEmail(String email) {
|
||||
return userMapper.updateLastOnlineTimeByEmail(email);
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,7 @@ 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.ArticleSearchDTO;
|
||||
import com.rymcu.forest.dto.CommentDTO;
|
||||
import com.rymcu.forest.dto.UserSearchDTO;
|
||||
import com.rymcu.forest.dto.*;
|
||||
import com.rymcu.forest.dto.admin.TopicTagDTO;
|
||||
import com.rymcu.forest.dto.admin.UserRoleDTO;
|
||||
import com.rymcu.forest.entity.*;
|
||||
@ -47,8 +44,8 @@ public class AdminController {
|
||||
@GetMapping("/users")
|
||||
public GlobalResult<Map<String, Object>> users(@RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "10") Integer rows, UserSearchDTO searchDTO){
|
||||
PageHelper.startPage(page, rows);
|
||||
List<User> list = userService.findUsers(searchDTO);
|
||||
PageInfo<User> pageInfo = new PageInfo<>(list);
|
||||
List<UserInfoDTO> list = userService.findUsers(searchDTO);
|
||||
PageInfo<UserInfoDTO> pageInfo = new PageInfo<>(list);
|
||||
Map<String, Object> map = new HashMap<String, Object>(2);
|
||||
map.put("users", pageInfo.getList());
|
||||
Map pagination = Utils.getPagination(pageInfo);
|
||||
|
@ -2,10 +2,11 @@ package com.rymcu.forest.web.api.exception;
|
||||
|
||||
public enum ErrorCode {
|
||||
|
||||
UNAUTHORIZED(401, "请求要求用户的身份认证"),//未认证(签名错误)
|
||||
UNAUTHORIZED(401, "请求要求用户的身份认证"),
|
||||
INVALID_TOKEN(402, "TOKEN验证失败,无效的TOKEN!"),
|
||||
TOKEN_(402, "TOKEN验证失败,无效的TOKEN!"),
|
||||
NOT_FOUND(404, "此接口不存在"),//接口不存在
|
||||
ACCESS_DENIED(403, "服务器拒绝请求!"),
|
||||
NOT_FOUND(404, "此接口不存在"),
|
||||
INTERNAL_SERVER_ERROR(500, "服务内部异常");
|
||||
|
||||
private int code;
|
||||
|
@ -17,6 +17,7 @@
|
||||
<result column="phone" property="phone"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="last_login_time" property="lastLoginTime"/>
|
||||
<result column="last_online_time" property="lastOnlineTime"/>
|
||||
<result column="created_time" property="createdTime"/>
|
||||
<result column="updated_time" property="updatedTime"/>
|
||||
</resultMap>
|
||||
@ -31,6 +32,8 @@
|
||||
<result column="phone" property="phone"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="last_login_time" property="lastLoginTime"/>
|
||||
<result column="last_online_time" property="lastOnlineTime"/>
|
||||
<result column="created_time" property="createdTime"/>
|
||||
<result column="signature" property="signature"/>
|
||||
</resultMap>
|
||||
<resultMap id="DTOResultMapper" type="com.rymcu.forest.dto.UserDTO">
|
||||
@ -60,10 +63,7 @@
|
||||
update forest_user set status = #{status} where id = #{idUser}
|
||||
</update>
|
||||
<update id="updateUserInfo">
|
||||
update forest_user set nickname = #{nickname},email = #{email},signature = #{signature},avatar_type = #{avatarType},avatar_url = #{avatarUrl},sex = #{sex}
|
||||
<if test="phone != null and phone != ''">
|
||||
,phone = #{phone}
|
||||
</if>
|
||||
update forest_user set nickname = #{nickname},signature = #{signature},avatar_type = #{avatarType},avatar_url = #{avatarUrl},sex = #{sex}
|
||||
where id = #{idUser}
|
||||
</update>
|
||||
<update id="updateLastLoginTime">
|
||||
@ -75,12 +75,15 @@
|
||||
<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>
|
||||
|
||||
<select id="findByAccount" resultMap="BaseResultMap">
|
||||
select id, nickname, account, password, status, avatar_type, avatar_url from forest_user where (account = #{account} or email = #{account} ) and status = 0
|
||||
</select>
|
||||
<select id="findUserInfoByAccount" resultMap="UserInfoResultMapper">
|
||||
select id, nickname, sex, avatar_type, avatar_url, email, phone, account, status, signature, last_login_time from forest_user where account = #{account}
|
||||
select id, nickname, sex, avatar_type, avatar_url, email, phone, account, status, signature, last_login_time, last_online_time from forest_user where account = #{account}
|
||||
</select>
|
||||
<select id="selectUserDTOByAccount" resultMap="DTOResultMapper">
|
||||
select id, nickname, avatar_type, avatar_url, account, signature from forest_user where account = #{account} and status = 0
|
||||
@ -92,7 +95,7 @@
|
||||
select count(*) from forest_user where nickname = #{nickname}
|
||||
</select>
|
||||
<select id="selectUserInfo" resultMap="UserInfoResultMapper">
|
||||
select id, nickname, sex, avatar_type, avatar_url, email, phone, account, status, signature, last_login_time from forest_user where id = #{idUser}
|
||||
select id, nickname, sex, avatar_type, avatar_url, email, phone, account, status, signature, last_login_time, last_online_time from forest_user where id = #{idUser}
|
||||
</select>
|
||||
<select id="checkNicknameByIdUser" resultType="java.lang.Integer">
|
||||
select count(*) from forest_user where nickname = #{nickname} and id != #{idUser}
|
||||
@ -100,14 +103,18 @@
|
||||
<select id="selectAuthor" resultMap="AuthorResultMap">
|
||||
select * from forest_user where id = #{id}
|
||||
</select>
|
||||
<select id="selectUsers" resultMap="BaseResultMap">
|
||||
select id, nickname, sex, avatar_type, avatar_url, email, account, status, last_login_time, created_time from forest_user
|
||||
<select id="selectUsers" resultMap="UserInfoResultMapper">
|
||||
select id, nickname, sex, avatar_type, avatar_url, email, account, status, last_login_time, created_time, last_online_time from forest_user
|
||||
<where>
|
||||
<if test="searchDTO.nickname != null and searchDTO.nickname != ''">
|
||||
and instr(nickname, #{searchDTO.nickname}) > 0
|
||||
</if>
|
||||
</where>
|
||||
order by last_login_time desc
|
||||
order by last_online_time desc
|
||||
</select>
|
||||
<select id="hasAdminPermission" resultType="java.lang.Boolean">
|
||||
select if(count(fur.id_role) = 0, false, true) from forest_user_role fur join forest_user fu on fur.id_user = fu.id
|
||||
where fu.email = #{email} and exists(select id_role from forest_role fr where instr(fr.input_code, 'admin') > 0 and fr.id = fur.id_role)
|
||||
</select>
|
||||
|
||||
</mapper>
|
Loading…
Reference in New Issue
Block a user