Commit fd9fb2a6 authored by dqjdda's avatar dqjdda
Browse files

Merge branch '2.3dev'

parents 7895e547 1839ef8d
......@@ -3,76 +3,47 @@ package me.zhengjie.modules.quartz.service;
import me.zhengjie.modules.quartz.domain.QuartzJob;
import me.zhengjie.modules.quartz.domain.QuartzLog;
import me.zhengjie.modules.quartz.service.dto.JobQueryCriteria;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Pageable;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* @author Zheng Jie
* @date 2019-01-07
*/
@CacheConfig(cacheNames = "quartzJob")
public interface QuartzJobService {
/**
* queryAll quartzJob
* @param criteria
* @param pageable
* @return
*/
@Cacheable
Object queryAll(JobQueryCriteria criteria, Pageable pageable);
/**
* queryAll quartzLog
* @param criteria
* @param pageable
* @return
*/
List<QuartzJob> queryAll(JobQueryCriteria criteria);
Object queryAllLog(JobQueryCriteria criteria, Pageable pageable);
/**
* create
* @param resources
* @return
*/
@CacheEvict(allEntries = true)
List<QuartzLog> queryAllLog(JobQueryCriteria criteria);
QuartzJob create(QuartzJob resources);
/**
* update
* @param resources
* @return
*/
@CacheEvict(allEntries = true)
void update(QuartzJob resources);
/**
* del
* @param quartzJob
*/
@CacheEvict(allEntries = true)
void delete(QuartzJob quartzJob);
/**
* findById
* @param id
* @return
*/
@Cacheable(key = "#p0")
QuartzJob findById(Long id);
/**
* 更改定时任务状态
* @param quartzJob
* @param quartzJob /
*/
@CacheEvict(allEntries = true)
void updateIsPause(QuartzJob quartzJob);
/**
* 立即执行定时任务
* @param quartzJob
* @param quartzJob /
*/
void execution(QuartzJob quartzJob);
void download(List<QuartzJob> queryAll, HttpServletResponse response) throws IOException;
void downloadLog(List<QuartzLog> queryAllLog, HttpServletResponse response) throws IOException;
}
......@@ -3,6 +3,8 @@ package me.zhengjie.modules.quartz.service.dto;
import lombok.Data;
import me.zhengjie.annotation.Query;
import java.sql.Timestamp;
/**
* @author Zheng Jie
* @date 2019-6-4 10:33:02
......@@ -15,4 +17,10 @@ public class JobQueryCriteria {
@Query
private Boolean isSuccess;
@Query(type = Query.Type.GREATER_THAN,propName = "createTime")
private Timestamp startTime;
@Query(type = Query.Type.LESS_THAN,propName = "createTime")
private Timestamp endTime;
}
......@@ -2,40 +2,55 @@ package me.zhengjie.modules.quartz.service.impl;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.quartz.domain.QuartzJob;
import me.zhengjie.modules.quartz.domain.QuartzLog;
import me.zhengjie.modules.quartz.repository.QuartzJobRepository;
import me.zhengjie.modules.quartz.repository.QuartzLogRepository;
import me.zhengjie.modules.quartz.service.QuartzJobService;
import me.zhengjie.modules.quartz.service.dto.JobQueryCriteria;
import me.zhengjie.modules.quartz.utils.QuartzManage;
import me.zhengjie.utils.FileUtil;
import me.zhengjie.utils.PageUtil;
import me.zhengjie.utils.QueryHelp;
import me.zhengjie.utils.ValidationUtil;
import org.quartz.CronExpression;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* @author Zheng Jie
* @date 2019-01-07
*/
@Service(value = "quartzJobService")
@CacheConfig(cacheNames = "quartzJob")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class QuartzJobServiceImpl implements QuartzJobService {
@Autowired
private QuartzJobRepository quartzJobRepository;
private final QuartzJobRepository quartzJobRepository;
private final QuartzLogRepository quartzLogRepository;
@Autowired
private QuartzLogRepository quartzLogRepository;
private final QuartzManage quartzManage;
@Autowired
private QuartzManage quartzManage;
public QuartzJobServiceImpl(QuartzJobRepository quartzJobRepository, QuartzLogRepository quartzLogRepository, QuartzManage quartzManage) {
this.quartzJobRepository = quartzJobRepository;
this.quartzLogRepository = quartzLogRepository;
this.quartzManage = quartzManage;
}
@Override
@Cacheable
public Object queryAll(JobQueryCriteria criteria, Pageable pageable){
return PageUtil.toPage(quartzJobRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable));
}
......@@ -46,13 +61,25 @@ public class QuartzJobServiceImpl implements QuartzJobService {
}
@Override
public List<QuartzJob> queryAll(JobQueryCriteria criteria) {
return quartzJobRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder));
}
@Override
public List<QuartzLog> queryAllLog(JobQueryCriteria criteria) {
return quartzLogRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder));
}
@Override
@Cacheable(key = "#p0")
public QuartzJob findById(Long id) {
Optional<QuartzJob> quartzJob = quartzJobRepository.findById(id);
ValidationUtil.isNull(quartzJob,"QuartzJob","id",id);
return quartzJob.get();
QuartzJob quartzJob = quartzJobRepository.findById(id).orElseGet(QuartzJob::new);
ValidationUtil.isNull(quartzJob.getId(),"QuartzJob","id",id);
return quartzJob;
}
@Override
@CacheEvict(allEntries = true)
@Transactional(rollbackFor = Exception.class)
public QuartzJob create(QuartzJob resources) {
if (!CronExpression.isValidExpression(resources.getCronExpression())){
......@@ -64,6 +91,7 @@ public class QuartzJobServiceImpl implements QuartzJobService {
}
@Override
@CacheEvict(allEntries = true)
@Transactional(rollbackFor = Exception.class)
public void update(QuartzJob resources) {
if(resources.getId().equals(1L)){
......@@ -77,6 +105,7 @@ public class QuartzJobServiceImpl implements QuartzJobService {
}
@Override
@CacheEvict(allEntries = true)
public void updateIsPause(QuartzJob quartzJob) {
if(quartzJob.getId().equals(1L)){
throw new BadRequestException("该任务不可操作");
......@@ -100,6 +129,7 @@ public class QuartzJobServiceImpl implements QuartzJobService {
}
@Override
@CacheEvict(allEntries = true)
@Transactional(rollbackFor = Exception.class)
public void delete(QuartzJob quartzJob) {
if(quartzJob.getId().equals(1L)){
......@@ -108,4 +138,41 @@ public class QuartzJobServiceImpl implements QuartzJobService {
quartzManage.deleteJob(quartzJob);
quartzJobRepository.delete(quartzJob);
}
@Override
public void download(List<QuartzJob> quartzJobs, HttpServletResponse response) throws IOException {
List<Map<String, Object>> list = new ArrayList<>();
for (QuartzJob quartzJob : quartzJobs) {
Map<String,Object> map = new LinkedHashMap<>();
map.put("任务名称", quartzJob.getJobName());
map.put("Bean名称", quartzJob.getBeanName());
map.put("执行方法", quartzJob.getMethodName());
map.put("参数", quartzJob.getParams());
map.put("表达式", quartzJob.getCronExpression());
map.put("状态", quartzJob.getIsPause() ? "暂停中" : "运行中");
map.put("描述", quartzJob.getRemark());
map.put("创建日期", quartzJob.getCreateTime());
list.add(map);
}
FileUtil.downloadExcel(list, response);
}
@Override
public void downloadLog(List<QuartzLog> queryAllLog, HttpServletResponse response) throws IOException {
List<Map<String, Object>> list = new ArrayList<>();
for (QuartzLog quartzLog : queryAllLog) {
Map<String,Object> map = new LinkedHashMap<>();
map.put("任务名称", quartzLog.getJobName());
map.put("Bean名称", quartzLog.getBeanName());
map.put("执行方法", quartzLog.getMethodName());
map.put("参数", quartzLog.getParams());
map.put("表达式", quartzLog.getCronExpression());
map.put("异常详情", quartzLog.getExceptionDetail());
map.put("耗时/毫秒", quartzLog.getTime());
map.put("状态", quartzLog.getIsSuccess() ? "成功" : "失败");
map.put("创建日期", quartzLog.getCreateTime());
list.add(map);
}
FileUtil.downloadExcel(list, response);
}
}
package me.zhengjie.modules.quartz.task;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.exception.BadRequestException;
import org.springframework.stereotype.Component;
/**
......
package me.zhengjie.modules.quartz.task;
import me.zhengjie.modules.monitor.service.VisitsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
......@@ -11,8 +10,11 @@ import org.springframework.stereotype.Component;
@Component
public class VisitsTask {
@Autowired
private VisitsService visitsService;
private final VisitsService visitsService;
public VisitsTask(VisitsService visitsService) {
this.visitsService = visitsService;
}
public void run(){
visitsService.save();
......
package me.zhengjie.modules.quartz.utils;
import me.zhengjie.config.thread.TheadFactoryName;
import me.zhengjie.config.thread.ThreadPoolExecutorUtil;
import me.zhengjie.modules.quartz.domain.QuartzJob;
import me.zhengjie.modules.quartz.domain.QuartzLog;
import me.zhengjie.modules.quartz.repository.QuartzLogRepository;
......@@ -11,13 +13,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.quartz.QuartzJobBean;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.*;
/**
* 参考人人开源,https://gitee.com/renrenio/renren-security
* @author
* @author /
* @date 2019-01-07
*/
@Async
......@@ -25,16 +25,16 @@ public class ExecutionJob extends QuartzJobBean {
private Logger logger = LoggerFactory.getLogger(this.getClass());
// 建议自定义线程池实现方式,该处仅供参考
private ExecutorService executorService = Executors.newSingleThreadExecutor();
// 该处仅供参考
private final static ThreadPoolExecutor executor = ThreadPoolExecutorUtil.getPoll();
@Override
@SuppressWarnings("unchecked")
protected void executeInternal(JobExecutionContext context) {
QuartzJob quartzJob = (QuartzJob) context.getMergedJobDataMap().get(QuartzJob.JOB_KEY);
// 获取spring bean
QuartzLogRepository quartzLogRepository = SpringContextHolder.getBean("quartzLogRepository");
QuartzJobService quartzJobService = SpringContextHolder.getBean("quartzJobService");
QuartzManage quartzManage = SpringContextHolder.getBean("quartzManage");
QuartzLogRepository quartzLogRepository = SpringContextHolder.getBean(QuartzLogRepository.class);
QuartzJobService quartzJobService = SpringContextHolder.getBean(QuartzJobService.class);
QuartzLog log = new QuartzLog();
log.setJobName(quartzJob.getJobName());
......@@ -48,7 +48,7 @@ public class ExecutionJob extends QuartzJobBean {
logger.info("任务准备执行,任务名称:{}", quartzJob.getJobName());
QuartzRunnable task = new QuartzRunnable(quartzJob.getBeanName(), quartzJob.getMethodName(),
quartzJob.getParams());
Future<?> future = executorService.submit(task);
Future<?> future = executor.submit(task);
future.get();
long times = System.currentTimeMillis() - startTime;
log.setTime(times);
......
......@@ -56,8 +56,7 @@ public class QuartzManage {
/**
* 更新job cron表达式
* @param quartzJob
* @throws SchedulerException
* @param quartzJob /
*/
public void updateJobCron(QuartzJob quartzJob){
try {
......@@ -88,8 +87,7 @@ public class QuartzManage {
/**
* 删除一个job
* @param quartzJob
* @throws SchedulerException
* @param quartzJob /
*/
public void deleteJob(QuartzJob quartzJob){
try {
......@@ -104,8 +102,7 @@ public class QuartzManage {
/**
* 恢复一个job
* @param quartzJob
* @throws SchedulerException
* @param quartzJob /
*/
public void resumeJob(QuartzJob quartzJob){
try {
......@@ -124,8 +121,7 @@ public class QuartzManage {
/**
* 立即执行job
* @param quartzJob
* @throws SchedulerException
* @param quartzJob /
*/
public void runAJobNow(QuartzJob quartzJob){
try {
......@@ -146,8 +142,7 @@ public class QuartzManage {
/**
* 暂停一个job
* @param quartzJob
* @throws SchedulerException
* @param quartzJob /
*/
public void pauseJob(QuartzJob quartzJob){
try {
......
package me.zhengjie.modules.quartz.utils;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.utils.SpringContextHolder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ReflectionUtils;
......@@ -10,7 +9,7 @@ import java.util.concurrent.Callable;
/**
* 执行定时任务
* @author
* @author /
*/
@Slf4j
public class QuartzRunnable implements Callable {
......
package me.zhengjie.modules.security.config;
import me.zhengjie.annotation.AnonymousAccess;
import me.zhengjie.config.ElPermissionConfig;
import me.zhengjie.modules.security.security.JwtAuthenticationEntryPoint;
import me.zhengjie.modules.security.security.JwtAuthorizationTokenFilter;
import me.zhengjie.modules.security.service.JwtUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
......@@ -19,29 +23,37 @@ import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
private final JwtAuthenticationEntryPoint unauthorizedHandler;
@Autowired
private JwtUserDetailsService jwtUserDetailsService;
private final JwtUserDetailsService jwtUserDetailsService;
/**
* 自定义基于JWT的安全过滤器
*/
@Autowired
JwtAuthorizationTokenFilter authenticationTokenFilter;
private final ApplicationContext applicationContext;
// 自定义基于JWT的安全过滤器
private final JwtAuthorizationTokenFilter authenticationTokenFilter;
@Value("${jwt.header}")
private String tokenHeader;
@Value("${jwt.auth.path}")
private String loginPath;
public SecurityConfig(JwtAuthenticationEntryPoint unauthorizedHandler, JwtUserDetailsService jwtUserDetailsService, JwtAuthorizationTokenFilter authenticationTokenFilter, ApplicationContext applicationContext) {
this.unauthorizedHandler = unauthorizedHandler;
this.jwtUserDetailsService = jwtUserDetailsService;
this.authenticationTokenFilter = authenticationTokenFilter;
this.applicationContext = applicationContext;
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
......@@ -69,18 +81,26 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// 搜寻 匿名标记 url: PreAuthorize("hasAnyRole('anonymous')") 和 PreAuthorize("@el.check('anonymous')") 和 AnonymousAccess
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
Set<String> anonymousUrls = new HashSet<>();
for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) {
HandlerMethod handlerMethod = infoEntry.getValue();
AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
PreAuthorize preAuthorize = handlerMethod.getMethodAnnotation(PreAuthorize.class);
if (null != preAuthorize && preAuthorize.value().contains("anonymous")) {
anonymousUrls.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
} else if (null != anonymousAccess && null == preAuthorize) {
anonymousUrls.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
}
}
httpSecurity
// 禁用 CSRF
.csrf().disable()
// 授权异常
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// 不创建会话
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 过滤请求
.authorizeRequests()
.antMatchers(
......@@ -90,35 +110,24 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
"/**/*.css",
"/**/*.js"
).anonymous()
.antMatchers( HttpMethod.POST,"/auth/"+loginPath).anonymous()
.antMatchers("/auth/vCode").anonymous()
// 支付宝回调
.antMatchers("/api/aliPay/return").anonymous()
.antMatchers("/api/aliPay/notify").anonymous()
// swagger start
.antMatchers("/swagger-ui.html").anonymous()
.antMatchers("/swagger-resources/**").anonymous()
.antMatchers("/webjars/**").anonymous()
.antMatchers("/*/api-docs").anonymous()
.antMatchers("/swagger-ui.html").permitAll()
.antMatchers("/swagger-resources/**").permitAll()
.antMatchers("/webjars/**").permitAll()
.antMatchers("/*/api-docs").permitAll()
// swagger end
// 接口限流测试
.antMatchers("/test/**").anonymous()
// 文件
.antMatchers("/avatar/**").anonymous()
.antMatchers("/file/**").anonymous()
.antMatchers("/avatar/**").permitAll()
.antMatchers("/file/**").permitAll()
// 放行OPTIONS请求
.antMatchers(HttpMethod.OPTIONS, "/**").anonymous()
.antMatchers("/druid/**").anonymous()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/druid/**").permitAll()
// 自定义匿名访问所有url放行 : 允许 匿名和带权限以及登录用户访问
.antMatchers(anonymousUrls.toArray(new String[0])).permitAll()
// 所有请求都需要认证
.anyRequest().authenticated()
// 防止iframe 造成跨域
.and().headers().frameOptions().disable();
httpSecurity
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
......
package me.zhengjie.modules.security.rest;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.IdUtil;
import com.wf.captcha.ArithmeticCaptcha;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.annotation.AnonymousAccess;
import me.zhengjie.aop.log.Log;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.monitor.service.RedisService;
import me.zhengjie.modules.security.security.AuthenticationInfo;
import me.zhengjie.modules.security.security.AuthorizationUser;
import me.zhengjie.modules.security.security.AuthInfo;
import me.zhengjie.modules.security.security.AuthUser;
import me.zhengjie.modules.security.security.ImgResult;
import me.zhengjie.modules.security.security.JwtUser;
import me.zhengjie.modules.security.utils.VerifyCodeUtils;
import me.zhengjie.modules.security.service.OnlineUserService;
import me.zhengjie.utils.EncryptUtils;
import me.zhengjie.modules.security.utils.JwtTokenUtil;
import me.zhengjie.utils.SecurityUtils;
import me.zhengjie.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
/**
* @author Zheng Jie
......@@ -34,30 +35,33 @@ import java.io.IOException;
*/
@Slf4j
@RestController
@RequestMapping("auth")
@RequestMapping("/auth")
@Api(tags = "系统:系统授权接口")
public class AuthenticationController {
@Value("${jwt.header}")
private String tokenHeader;
@Value("${jwt.codeKey}")
private String codeKey;
@Autowired
private JwtTokenUtil jwtTokenUtil;
private final JwtTokenUtil jwtTokenUtil;
@Autowired
private RedisService redisService;
private final RedisService redisService;
@Autowired
@Qualifier("jwtUserDetailsService")
private UserDetailsService userDetailsService;
private final UserDetailsService userDetailsService;
private final OnlineUserService onlineUserService;
public AuthenticationController(JwtTokenUtil jwtTokenUtil, RedisService redisService, @Qualifier("jwtUserDetailsService") UserDetailsService userDetailsService, OnlineUserService onlineUserService) {
this.jwtTokenUtil = jwtTokenUtil;
this.redisService = redisService;
this.userDetailsService = userDetailsService;
this.onlineUserService = onlineUserService;
}
/**
* 登录授权
* @param authorizationUser
* @return
*/
@Log("用户登录")
@PostMapping(value = "${jwt.auth.path}")
public ResponseEntity login(@Validated @RequestBody AuthorizationUser authorizationUser){
@ApiOperation("登录授权")
@AnonymousAccess
@PostMapping(value = "/login")
public ResponseEntity login(@Validated @RequestBody AuthUser authorizationUser, HttpServletRequest request){
// 查询验证码
String code = redisService.getCodeVal(authorizationUser.getUuid());
......@@ -78,45 +82,41 @@ public class AuthenticationController {
if(!jwtUser.isEnabled()){
throw new AccountExpiredException("账号已停用,请联系管理员");
}
// 生成令牌
final String token = jwtTokenUtil.generateToken(jwtUser);
// 保存在线信息
onlineUserService.save(jwtUser, token, request);
// 返回 token
return ResponseEntity.ok(new AuthenticationInfo(token,jwtUser));
return ResponseEntity.ok(new AuthInfo(token,jwtUser));
}
/**
* 获取用户信息
* @return
*/
@GetMapping(value = "${jwt.auth.account}")
@ApiOperation("获取用户信息")
@GetMapping(value = "/info")
public ResponseEntity getUserInfo(){
JwtUser jwtUser = (JwtUser)userDetailsService.loadUserByUsername(SecurityUtils.getUsername());
return ResponseEntity.ok(jwtUser);
}
/**
* 获取验证码
*/
@GetMapping(value = "vCode")
public ImgResult getCode(HttpServletResponse response) throws IOException {
@ApiOperation("获取验证码")
@AnonymousAccess
@GetMapping(value = "/code")
public ImgResult getCode(){
// 算术类型 https://gitee.com/whvse/EasyCaptcha
ArithmeticCaptcha captcha = new ArithmeticCaptcha(111, 36);
// 几位数运算,默认是两位
captcha.setLen(2);
// 获取运算的结果:5
String result = captcha.text();
String uuid = codeKey + IdUtil.simpleUUID();
redisService.saveCode(uuid,result);
return new ImgResult(captcha.toBase64(),uuid);
}
//生成随机字串
String verifyCode = VerifyCodeUtils.generateVerifyCode(4);
String uuid = IdUtil.simpleUUID();
redisService.saveCode(uuid,verifyCode);
// 生成图片
int w = 111, h = 36;
ByteArrayOutputStream stream = new ByteArrayOutputStream();
VerifyCodeUtils.outputImage(w, h, stream, verifyCode);
try {
return new ImgResult(Base64.encode(stream.toByteArray()),uuid);
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
stream.close();
}
@ApiOperation("退出登录")
@AnonymousAccess
@DeleteMapping(value = "/logout")
public ResponseEntity logout(HttpServletRequest request){
onlineUserService.logout(jwtTokenUtil.getToken(request));
return new ResponseEntity(HttpStatus.OK);
}
}
package me.zhengjie.modules.security.rest;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import me.zhengjie.aop.log.Log;
import me.zhengjie.modules.security.service.OnlineUserService;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@RestController
@RequestMapping("/auth/online")
@Api(tags = "系统:在线用户管理")
public class OnlineController {
private final OnlineUserService onlineUserService;
public OnlineController(OnlineUserService onlineUserService) {
this.onlineUserService = onlineUserService;
}
@ApiOperation("查询在线用户")
@GetMapping
@PreAuthorize("@el.check()")
public ResponseEntity getAll(String filter, Pageable pageable){
return new ResponseEntity<>(onlineUserService.getAll(filter, pageable),HttpStatus.OK);
}
@Log("导出数据")
@ApiOperation("导出数据")
@GetMapping(value = "/download")
@PreAuthorize("@el.check()")
public void download(HttpServletResponse response, String filter) throws IOException {
onlineUserService.download(onlineUserService.getAll(filter), response);
}
@ApiOperation("踢出用户")
@DeleteMapping(value = "/{key}")
@PreAuthorize("@el.check()")
public ResponseEntity delete(@PathVariable String key) throws Exception {
onlineUserService.kickOut(key);
return new ResponseEntity(HttpStatus.OK);
}
}
package me.zhengjie.modules.security.security;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.io.Serializable;
/**
* @author Zheng Jie
* @date 2018-11-23
* 返回token
*/
@Getter
@AllArgsConstructor
public class AuthenticationInfo implements Serializable {
private final String token;
private final JwtUser user;
}
package me.zhengjie.modules.security.security;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.io.Serializable;
/**
* @author Zheng Jie
* @date 2018-11-23
* 返回token
*/
@Getter
@AllArgsConstructor
public class AuthInfo implements Serializable {
private final String token;
private final JwtUser user;
}
......@@ -11,7 +11,7 @@ import javax.validation.constraints.NotBlank;
*/
@Getter
@Setter
public class AuthorizationUser {
public class AuthUser {
@NotBlank
private String username;
......
......@@ -18,9 +18,7 @@ public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Se
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
/**
* 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应
*/
// 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException==null?"Unauthorized":authException.getMessage());
}
}
......@@ -3,11 +3,12 @@ package me.zhengjie.modules.security.security;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.modules.security.utils.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
......@@ -22,38 +23,33 @@ import java.io.IOException;
@Component
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {
@Value("${jwt.online}")
private String onlineKey;
private final UserDetailsService userDetailsService;
private final JwtTokenUtil jwtTokenUtil;
private final String tokenHeader;
private final RedisTemplate redisTemplate;
public JwtAuthorizationTokenFilter(@Qualifier("jwtUserDetailsService") UserDetailsService userDetailsService, JwtTokenUtil jwtTokenUtil, @Value("${jwt.header}") String tokenHeader) {
public JwtAuthorizationTokenFilter(@Qualifier("jwtUserDetailsService") UserDetailsService userDetailsService, JwtTokenUtil jwtTokenUtil, RedisTemplate redisTemplate) {
this.userDetailsService = userDetailsService;
this.jwtTokenUtil = jwtTokenUtil;
this.tokenHeader = tokenHeader;
this.redisTemplate = redisTemplate;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
final String requestHeader = request.getHeader(this.tokenHeader);
String username = null;
String authToken = null;
if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
authToken = requestHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(authToken);
} catch (ExpiredJwtException e) {
log.error(e.getMessage());
}
String authToken = jwtTokenUtil.getToken(request);
OnlineUser onlineUser = null;
try {
onlineUser = (OnlineUser)redisTemplate.opsForValue().get(onlineKey + authToken);
} catch (ExpiredJwtException e) {
log.error(e.getMessage());
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
if (onlineUser != null && SecurityContextHolder.getContext().getAuthentication() == null) {
// It is not compelling necessary to load the use details from the database. You could also store the information
// in the token and read it from it. It's up to you ;)
JwtUser userDetails = (JwtUser)this.userDetailsService.loadUserByUsername(username);
JwtUser userDetails = (JwtUser)this.userDetailsService.loadUserByUsername(onlineUser.getUserName());
// For simple validation it is completely sufficient to just check the token integrity. You don't have to call
// the database compellingly. Again it's up to you ;)
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
......
package me.zhengjie.modules.security.security;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author Zheng Jie
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OnlineUser {
private String userName;
private String job;
private String browser;
private String ip;
private String address;
private String key;
private Date loginTime;
}
package me.zhengjie.modules.security.service;
import me.zhengjie.modules.system.domain.Menu;
import me.zhengjie.modules.system.domain.Role;
import me.zhengjie.modules.system.repository.RoleRepository;
import me.zhengjie.modules.system.service.dto.UserDTO;
import org.springframework.beans.factory.annotation.Autowired;
import me.zhengjie.utils.StringUtils;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
......@@ -17,13 +19,16 @@ import java.util.stream.Collectors;
@CacheConfig(cacheNames = "role")
public class JwtPermissionService {
@Autowired
private RoleRepository roleRepository;
private final RoleRepository roleRepository;
public JwtPermissionService(RoleRepository roleRepository) {
this.roleRepository = roleRepository;
}
/**
* key的名称如有修改,请同步修改 UserServiceImpl 中的 update 方法
* @param user
* @return
* @param user 用户信息
* @return Collection
*/
@Cacheable(key = "'loadPermissionByUser:' + #p0.username")
public Collection<GrantedAuthority> mapToGrantedAuthorities(UserDTO user) {
......@@ -31,9 +36,13 @@ public class JwtPermissionService {
System.out.println("--------------------loadPermissionByUser:" + user.getUsername() + "---------------------");
Set<Role> roles = roleRepository.findByUsers_Id(user.getId());
return roles.stream().flatMap(role -> role.getPermissions().stream())
.map(permission -> new SimpleGrantedAuthority(permission.getName()))
Set<String> permissions = roles.stream().filter(role -> StringUtils.isNotBlank(role.getPermission())).map(Role::getPermission).collect(Collectors.toSet());
permissions.addAll(
roles.stream().flatMap(role -> role.getMenus().stream())
.filter(menu -> StringUtils.isNotBlank(menu.getPermission()))
.map(Menu::getPermission).collect(Collectors.toSet())
);
return permissions.stream().map(permission -> new SimpleGrantedAuthority(permission))
.collect(Collectors.toList());
}
}
......@@ -4,7 +4,6 @@ import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.security.security.JwtUser;
import me.zhengjie.modules.system.service.UserService;
import me.zhengjie.modules.system.service.dto.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
......@@ -20,11 +19,14 @@ import java.util.Optional;
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class JwtUserDetailsService implements UserDetailsService {
@Autowired
private UserService userService;
private final UserService userService;
@Autowired
private JwtPermissionService permissionService;
private final JwtPermissionService permissionService;
public JwtUserDetailsService(UserService userService, JwtPermissionService permissionService) {
this.userService = userService;
this.permissionService = permissionService;
}
@Override
public UserDetails loadUserByUsername(String username){
......
package me.zhengjie.modules.security.service;
import me.zhengjie.modules.security.security.JwtUser;
import me.zhengjie.modules.security.security.OnlineUser;
import me.zhengjie.utils.EncryptUtils;
import me.zhengjie.utils.FileUtil;
import me.zhengjie.utils.PageUtil;
import me.zhengjie.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* @author Zheng Jie
* @Date 2019年10月26日21:56:27
*/
@Service
@SuppressWarnings({"unchecked","all"})
public class OnlineUserService {
@Value("${jwt.expiration}")
private Long expiration;
@Value("${jwt.online}")
private String onlineKey;
private final RedisTemplate redisTemplate;
public OnlineUserService(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void save(JwtUser jwtUser, String token, HttpServletRequest request){
String job = jwtUser.getDept() + "/" + jwtUser.getJob();
String ip = StringUtils.getIp(request);
String browser = StringUtils.getBrowser(request);
String address = StringUtils.getCityInfo(ip);
OnlineUser onlineUser = null;
try {
onlineUser = new OnlineUser(jwtUser.getUsername(), job, browser , ip, address, EncryptUtils.desEncrypt(token), new Date());
} catch (Exception e) {
e.printStackTrace();
}
redisTemplate.opsForValue().set(onlineKey + token, onlineUser);
redisTemplate.expire(onlineKey + token,expiration, TimeUnit.MILLISECONDS);
}
public Page<OnlineUser> getAll(String filter, Pageable pageable){
List<OnlineUser> onlineUsers = getAll(filter);
return new PageImpl<OnlineUser>(
PageUtil.toPage(pageable.getPageNumber(),pageable.getPageSize(),onlineUsers),
pageable,
onlineUsers.size());
}
public List<OnlineUser> getAll(String filter){
List<String> keys = new ArrayList<>(redisTemplate.keys(onlineKey + "*"));
Collections.reverse(keys);
List<OnlineUser> onlineUsers = new ArrayList<>();
for (String key : keys) {
OnlineUser onlineUser = (OnlineUser) redisTemplate.opsForValue().get(key);
if(StringUtils.isNotBlank(filter)){
if(onlineUser.toString().contains(filter)){
onlineUsers.add(onlineUser);
}
} else {
onlineUsers.add(onlineUser);
}
}
Collections.sort(onlineUsers, (o1, o2) -> {
return o2.getLoginTime().compareTo(o1.getLoginTime());
});
return onlineUsers;
}
public void kickOut(String val) throws Exception {
String key = onlineKey + EncryptUtils.desDecrypt(val);
redisTemplate.delete(key);
}
public void logout(String token) {
String key = onlineKey + token;
redisTemplate.delete(key);
}
public void download(List<OnlineUser> all, HttpServletResponse response) throws IOException {
List<Map<String, Object>> list = new ArrayList<>();
for (OnlineUser user : all) {
Map<String,Object> map = new LinkedHashMap<>();
map.put("用户名", user.getUserName());
map.put("岗位", user.getJob());
map.put("登录IP", user.getIp());
map.put("登录地点", user.getAddress());
map.put("浏览器", user.getBrowser());
map.put("登录日期", user.getLoginTime());
list.add(map);
}
FileUtil.downloadExcel(list, response);
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment