diff --git a/eladmin-common/src/main/java/me/zhengjie/config/SwaggerConfig.java b/eladmin-common/src/main/java/me/zhengjie/config/SwaggerConfig.java
index 4d4520e9..8f0d0376 100644
--- a/eladmin-common/src/main/java/me/zhengjie/config/SwaggerConfig.java
+++ b/eladmin-common/src/main/java/me/zhengjie/config/SwaggerConfig.java
@@ -39,6 +39,9 @@ public class SwaggerConfig {
@Value("${jwt.header}")
private String tokenHeader;
+ @Value("${jwt.token-start-with}")
+ private String tokenStartWith;
+
@Value("${swagger.enabled}")
private Boolean enabled;
@@ -50,7 +53,7 @@ public class SwaggerConfig {
ticketPar.name(tokenHeader).description("token")
.modelRef(new ModelRef("string"))
.parameterType("header")
- .defaultValue("Bearer ")
+ .defaultValue(tokenStartWith + " ")
.required(true)
.build();
pars.add(ticketPar.build());
diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/EncryptUtils.java b/eladmin-common/src/main/java/me/zhengjie/utils/EncryptUtils.java
index b9d28c3e..51fa8693 100644
--- a/eladmin-common/src/main/java/me/zhengjie/utils/EncryptUtils.java
+++ b/eladmin-common/src/main/java/me/zhengjie/utils/EncryptUtils.java
@@ -82,11 +82,4 @@ public class EncryptUtils {
}
return b2;
}
-
- /**
- * 密码加密
- */
- public static String encryptPassword(String password){
- return DigestUtils.md5DigestAsHex(password.getBytes());
- }
}
diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java b/eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java
index 0799c968..03bf4d01 100644
--- a/eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java
+++ b/eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java
@@ -3,6 +3,7 @@ package me.zhengjie.utils;
import cn.hutool.json.JSONObject;
import me.zhengjie.exception.BadRequestException;
import org.springframework.http.HttpStatus;
+import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
/**
@@ -15,7 +16,7 @@ public class SecurityUtils {
public static UserDetails getUserDetails() {
UserDetails userDetails;
try {
- userDetails = (UserDetails) org.springframework.security.core.context.SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+ userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
} catch (Exception e) {
throw new BadRequestException(HttpStatus.UNAUTHORIZED, "登录状态过期");
}
@@ -30,13 +31,4 @@ public class SecurityUtils {
Object obj = getUserDetails();
return new JSONObject(obj).get("username", String.class);
}
-
- /**
- * 获取系统用户id
- * @return 系统用户id
- */
- public static Long getUserId(){
- Object obj = getUserDetails();
- return new JSONObject(obj).get("id", Long.class);
- }
}
diff --git a/eladmin-system/pom.xml b/eladmin-system/pom.xml
index f3eda434..c08dd54c 100644
--- a/eladmin-system/pom.xml
+++ b/eladmin-system/pom.xml
@@ -13,7 +13,7 @@
核心模块
- 0.9.1
+ 0.10.6
@@ -45,7 +45,17 @@
io.jsonwebtoken
- jjwt
+ jjwt-api
+ ${jjwt.version}
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ ${jjwt.version}
+
+
+ io.jsonwebtoken
+ jjwt-jackson
${jjwt.version}
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/monitor/service/impl/RedisServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/monitor/service/impl/RedisServiceImpl.java
index 52dfe488..991ef865 100644
--- a/eladmin-system/src/main/java/me/zhengjie/modules/monitor/service/impl/RedisServiceImpl.java
+++ b/eladmin-system/src/main/java/me/zhengjie/modules/monitor/service/impl/RedisServiceImpl.java
@@ -1,8 +1,8 @@
package me.zhengjie.modules.monitor.service.impl;
-import cn.hutool.core.util.ObjectUtil;
import me.zhengjie.modules.monitor.domain.vo.RedisVo;
import me.zhengjie.modules.monitor.service.RedisService;
+import me.zhengjie.modules.security.config.SecurityProperties;
import me.zhengjie.utils.FileUtil;
import me.zhengjie.utils.PageUtil;
import org.springframework.beans.factory.annotation.Value;
@@ -27,18 +27,13 @@ import java.util.stream.Collectors;
public class RedisServiceImpl implements RedisService {
private final RedisTemplate redisTemplate;
-
+ private final SecurityProperties properties;
@Value("${loginCode.expiration}")
private Long expiration;
- @Value("${jwt.online}")
- private String onlineKey;
-
- @Value("${jwt.codeKey}")
- private String codeKey;
-
- public RedisServiceImpl(RedisTemplate redisTemplate) {
+ public RedisServiceImpl(RedisTemplate redisTemplate, SecurityProperties properties) {
this.redisTemplate = redisTemplate;
+ this.properties = properties;
}
@Override
@@ -59,7 +54,7 @@ public class RedisServiceImpl implements RedisService {
Set keys = redisTemplate.keys(key);
for (String s : keys) {
// 过滤掉权限的缓存
- if (s.contains("role::loadPermissionByUser") || s.contains("user::loadUserByUsername") || s.contains(onlineKey) || s.contains(codeKey)) {
+ if (s.contains("role::loadPermissionByUser") || s.contains("user::loadUserByUsername") || s.contains(properties.getOnlineKey()) || s.contains(properties.getCodeKey())) {
continue;
}
RedisVo redisVo = new RedisVo(s, Objects.requireNonNull(redisTemplate.opsForValue().get(s)).toString());
@@ -76,7 +71,7 @@ public class RedisServiceImpl implements RedisService {
@Override
public void deleteAll() {
Set keys = redisTemplate.keys( "*");
- redisTemplate.delete(keys.stream().filter(s -> !s.contains(onlineKey)).filter(s -> !s.contains(codeKey)).collect(Collectors.toList()));
+ redisTemplate.delete(keys.stream().filter(s -> !s.contains(properties.getOnlineKey())).filter(s -> !s.contains(properties.getCodeKey())).collect(Collectors.toList()));
}
@Override
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityConfig.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityConfig.java
index 55a021a2..eb8e5dd2 100644
--- a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityConfig.java
+++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityConfig.java
@@ -1,19 +1,12 @@
package me.zhengjie.modules.security.config;
import me.zhengjie.annotation.AnonymousAccess;
-import me.zhengjie.modules.security.security.JwtAccessDeniedHandler;
-import me.zhengjie.modules.security.security.JwtAuthenticationEntryPoint;
-import me.zhengjie.modules.security.security.JwtAuthorizationTokenFilter;
-import me.zhengjie.modules.security.service.JwtUserDetailsServiceImpl;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
+import me.zhengjie.modules.security.security.*;
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;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -23,6 +16,7 @@ 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.filter.CorsFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@@ -35,38 +29,23 @@ import java.util.Set;
*/
@Configuration
@EnableWebSecurity
-@EnableGlobalMethodSecurity(prePostEnabled = true)
+@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
- private final JwtAuthenticationEntryPoint unauthorizedHandler;
-
- private final JwtAccessDeniedHandler accessDeniedHandler;
-
- private final JwtUserDetailsServiceImpl jwtUserDetailsService;
-
+ private final TokenProvider tokenProvider;
+ private final CorsFilter corsFilter;
+ private final JwtAuthenticationEntryPoint authenticationErrorHandler;
+ private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
private final ApplicationContext applicationContext;
- /** 自定义基于JWT的安全过滤器 */
- private final JwtAuthorizationTokenFilter authenticationTokenFilter;
-
- @Value("${jwt.header}")
- private String tokenHeader;
-
- public SecurityConfig(JwtAuthenticationEntryPoint unauthorizedHandler, JwtAccessDeniedHandler accessDeniedHandler, JwtUserDetailsServiceImpl jwtUserDetailsService, JwtAuthorizationTokenFilter authenticationTokenFilter, ApplicationContext applicationContext) {
- this.unauthorizedHandler = unauthorizedHandler;
- this.accessDeniedHandler = accessDeniedHandler;
- this.jwtUserDetailsService = jwtUserDetailsService;
- this.authenticationTokenFilter = authenticationTokenFilter;
+ public SecurityConfig(TokenProvider tokenProvider, CorsFilter corsFilter, JwtAuthenticationEntryPoint authenticationErrorHandler, JwtAccessDeniedHandler jwtAccessDeniedHandler, ApplicationContext applicationContext) {
+ this.tokenProvider = tokenProvider;
+ this.corsFilter = corsFilter;
+ this.authenticationErrorHandler = authenticationErrorHandler;
+ this.jwtAccessDeniedHandler = jwtAccessDeniedHandler;
this.applicationContext = applicationContext;
}
- @Autowired
- public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
- auth
- .userDetailsService(jwtUserDetailsService)
- .passwordEncoder(passwordEncoderBean());
- }
-
@Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
// Remove the ROLE_ prefix
@@ -74,16 +53,10 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
@Bean
- public PasswordEncoder passwordEncoderBean() {
+ public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
- @Bean
- @Override
- public AuthenticationManager authenticationManagerBean() throws Exception {
- return super.authenticationManagerBean();
- }
-
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// 搜寻 匿名标记 url: PreAuthorize("hasAnyRole('anonymous')") 和 PreAuthorize("@el.check('anonymous')") 和 AnonymousAccess
@@ -102,12 +75,25 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
httpSecurity
// 禁用 CSRF
.csrf().disable()
+
+ .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
// 授权异常
- .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
- .exceptionHandling().accessDeniedHandler(accessDeniedHandler).and()
+ .exceptionHandling()
+ .authenticationEntryPoint(authenticationErrorHandler)
+ .accessDeniedHandler(jwtAccessDeniedHandler)
+
+ // 防止iframe 造成跨域
+ .and()
+ .headers()
+ .frameOptions()
+ .sameOrigin()
+
// 不创建会话
- .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
- // 过滤请求
+ .and()
+ .sessionManagement()
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+
+ .and()
.authorizeRequests()
.antMatchers(
HttpMethod.GET,
@@ -116,7 +102,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
"/**/*.css",
"/**/*.js",
"/webSocket/**"
- ).anonymous()
+ ).permitAll()
// swagger start
.antMatchers("/swagger-ui.html").permitAll()
.antMatchers("/swagger-resources/**").permitAll()
@@ -126,16 +112,18 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 文件
.antMatchers("/avatar/**").permitAll()
.antMatchers("/file/**").permitAll()
+ .antMatchers("/druid/**").permitAll()
// 放行OPTIONS请求
.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);
+ .and()
+ .apply(securityConfigurerAdapter());
+ }
+
+ private TokenConfigurer securityConfigurerAdapter() {
+ return new TokenConfigurer(tokenProvider);
}
}
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityProperties.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityProperties.java
new file mode 100644
index 00000000..d8688e74
--- /dev/null
+++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityProperties.java
@@ -0,0 +1,42 @@
+package me.zhengjie.modules.security.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+/**
+ * Jwt参数配置
+ * @author Zheng Jie
+ * @date 2019年11月28日
+ */
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "jwt")
+public class SecurityProperties {
+
+ /** Request Headers : Authorization */
+ private String header;
+
+ /** 令牌前缀,最后留个空格 Bearer */
+ private String tokenStartWith;
+
+ /** 必须使用最少88位的Base64对该令牌进行编码 */
+ private String base64Secret;
+
+ /** 令牌过期时间 此处单位/秒 */
+ private Long tokenValidityInSeconds;
+
+ /** 记住我模式下的令牌过期时间 此处单位/毫秒 */
+ private Long tokenValidityInSecondsForRememberMe;
+
+ /** 在线用户 key,根据 key 查询 redis 中在线用户的数据 */
+ private String onlineKey;
+
+ /** 验证码 key */
+ private String codeKey;
+
+ public String getTokenStartWith() {
+ return tokenStartWith + " ";
+ }
+}
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/rest/AuthenticationController.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/rest/AuthController.java
similarity index 54%
rename from eladmin-system/src/main/java/me/zhengjie/modules/security/rest/AuthenticationController.java
rename to eladmin-system/src/main/java/me/zhengjie/modules/security/rest/AuthController.java
index 0f30d7a8..7893c6f5 100644
--- a/eladmin-system/src/main/java/me/zhengjie/modules/security/rest/AuthenticationController.java
+++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/rest/AuthController.java
@@ -9,24 +9,25 @@ 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.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.config.SecurityProperties;
+import me.zhengjie.modules.security.security.TokenProvider;
+import me.zhengjie.modules.security.security.vo.AuthUser;
+import me.zhengjie.modules.security.security.vo.JwtUser;
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.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.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
/**
* @author Zheng Jie
@@ -37,24 +38,22 @@ import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping("/auth")
@Api(tags = "系统:系统授权接口")
-public class AuthenticationController {
-
- @Value("${jwt.codeKey}")
- private String codeKey;
-
- private final JwtTokenUtil jwtTokenUtil;
+public class AuthController {
+ private final SecurityProperties properties;
private final RedisService redisService;
-
private final UserDetailsService userDetailsService;
-
private final OnlineUserService onlineUserService;
+ private final TokenProvider tokenProvider;
+ private final AuthenticationManagerBuilder authenticationManagerBuilder;
- public AuthenticationController(JwtTokenUtil jwtTokenUtil, RedisService redisService, @Qualifier("jwtUserDetailsServiceImpl") UserDetailsService userDetailsService, OnlineUserService onlineUserService) {
- this.jwtTokenUtil = jwtTokenUtil;
+ public AuthController(SecurityProperties properties, RedisService redisService, UserDetailsService userDetailsService, OnlineUserService onlineUserService, TokenProvider tokenProvider, AuthenticationManagerBuilder authenticationManagerBuilder) {
+ this.properties = properties;
this.redisService = redisService;
this.userDetailsService = userDetailsService;
this.onlineUserService = onlineUserService;
+ this.tokenProvider = tokenProvider;
+ this.authenticationManagerBuilder = authenticationManagerBuilder;
}
@Log("用户登录")
@@ -62,7 +61,6 @@ public class AuthenticationController {
@AnonymousAccess
@PostMapping(value = "/login")
public ResponseEntity login(@Validated @RequestBody AuthUser authUser, HttpServletRequest request){
-
// 查询验证码
String code = redisService.getCodeVal(authUser.getUuid());
// 清除验证码
@@ -73,21 +71,23 @@ public class AuthenticationController {
if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
throw new BadRequestException("验证码错误");
}
- final JwtUser jwtUser = (JwtUser) userDetailsService.loadUserByUsername(authUser.getUsername());
+ UsernamePasswordAuthenticationToken authenticationToken =
+ new UsernamePasswordAuthenticationToken(authUser.getUsername(), authUser.getPassword());
- if(!jwtUser.getPassword().equals(EncryptUtils.encryptPassword(authUser.getPassword()))){
- throw new AccountExpiredException("密码错误");
- }
-
- if(!jwtUser.isEnabled()){
- throw new AccountExpiredException("账号已停用,请联系管理员");
- }
+ Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ boolean rememberMe = (authUser.getRememberMe() == null) ? false : authUser.getRememberMe();
// 生成令牌
- final String token = jwtTokenUtil.generateToken(jwtUser);
+ String token = tokenProvider.createToken(authentication, rememberMe);
+ final JwtUser jwtUser = (JwtUser) authentication.getPrincipal();
// 保存在线信息
onlineUserService.save(jwtUser, token, request);
- // 返回 token
- return ResponseEntity.ok(new AuthInfo(token,jwtUser));
+ // 返回 token 与 用户信息
+ Map authInfo = new HashMap(2){{
+ put("token", properties.getTokenStartWith() + token);
+ put("user", jwtUser);
+ }};
+ return ResponseEntity.ok(authInfo);
}
@ApiOperation("获取用户信息")
@@ -100,23 +100,28 @@ public class AuthenticationController {
@ApiOperation("获取验证码")
@AnonymousAccess
@GetMapping(value = "/code")
- public ImgResult getCode(){
+ public ResponseEntity 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();
+ String uuid = properties.getCodeKey() + IdUtil.simpleUUID();
redisService.saveCode(uuid,result);
- return new ImgResult(captcha.toBase64(),uuid);
+ // 验证码信息
+ Map imgResult = new HashMap(2){{
+ put("img", captcha.toBase64());
+ put("uuid", uuid);
+ }};
+ return ResponseEntity.ok(imgResult);
}
@ApiOperation("退出登录")
@AnonymousAccess
@DeleteMapping(value = "/logout")
public ResponseEntity logout(HttpServletRequest request){
- onlineUserService.logout(jwtTokenUtil.getToken(request));
+ onlineUserService.logout(tokenProvider.getToken(request));
return new ResponseEntity(HttpStatus.OK);
}
}
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/AuthInfo.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/AuthInfo.java
deleted file mode 100644
index 94bb2ab6..00000000
--- a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/AuthInfo.java
+++ /dev/null
@@ -1,19 +0,0 @@
-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;
-}
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/ImgResult.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/ImgResult.java
deleted file mode 100644
index 488ff9e7..00000000
--- a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/ImgResult.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package me.zhengjie.modules.security.security;
-
-import lombok.AllArgsConstructor;
-import lombok.Data;
-
-/**
- * @author Zheng Jie
- * @date 2019-6-5 17:29:57
- */
-@Data
-@AllArgsConstructor
-public class ImgResult {
-
- private String img;
-
- private String uuid;
-}
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAccessDeniedHandler.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAccessDeniedHandler.java
index d510e330..fc9ea698 100644
--- a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAccessDeniedHandler.java
+++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAccessDeniedHandler.java
@@ -8,6 +8,9 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
+/**
+ * @author Zheng Jie
+ */
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthenticationEntryPoint.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthenticationEntryPoint.java
index 61eb2c50..6f33d313 100644
--- a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthenticationEntryPoint.java
+++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthenticationEntryPoint.java
@@ -13,9 +13,7 @@ import java.io.Serializable;
* @author Zheng Jie
*/
@Component
-public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
-
- private static final long serialVersionUID = -8970718410437077606L;
+public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthorizationTokenFilter.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthorizationTokenFilter.java
deleted file mode 100644
index 7fe95574..00000000
--- a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthorizationTokenFilter.java
+++ /dev/null
@@ -1,66 +0,0 @@
-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.UserDetailsService;
-import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
-import org.springframework.stereotype.Component;
-import org.springframework.web.filter.OncePerRequestFilter;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-
-/**
- * @author Zheng Jie
- */
-@Slf4j
-@Component
-public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {
-
-
- @Value("${jwt.online}")
- private String onlineKey;
-
- private final UserDetailsService userDetailsService;
- private final JwtTokenUtil jwtTokenUtil;
- private final RedisTemplate redisTemplate;
-
- public JwtAuthorizationTokenFilter(@Qualifier("jwtUserDetailsServiceImpl") UserDetailsService userDetailsService, JwtTokenUtil jwtTokenUtil, RedisTemplate redisTemplate) {
- this.userDetailsService = userDetailsService;
- this.jwtTokenUtil = jwtTokenUtil;
- this.redisTemplate = redisTemplate;
- }
-
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
- String authToken = jwtTokenUtil.getToken(request);
- OnlineUser onlineUser = null;
- try {
- onlineUser = (OnlineUser)redisTemplate.opsForValue().get(onlineKey + authToken);
- } catch (ExpiredJwtException e) {
- log.error(e.getMessage());
- }
- 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(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)) {
- UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
- authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
- SecurityContextHolder.getContext().setAuthentication(authentication);
- }
- }
- chain.doFilter(request, response);
- }
-}
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenConfigurer.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenConfigurer.java
new file mode 100644
index 00000000..d78378d8
--- /dev/null
+++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenConfigurer.java
@@ -0,0 +1,24 @@
+package me.zhengjie.modules.security.security;
+
+import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.web.DefaultSecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+/**
+ * @author /
+ */
+public class TokenConfigurer extends SecurityConfigurerAdapter {
+
+ private final TokenProvider tokenProvider;
+
+ public TokenConfigurer(TokenProvider tokenProvider){
+ this.tokenProvider = tokenProvider;
+ }
+
+ @Override
+ public void configure(HttpSecurity http) {
+ TokenFilter customFilter = new TokenFilter(tokenProvider);
+ http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
+ }
+}
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenFilter.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenFilter.java
new file mode 100644
index 00000000..bc0a0de1
--- /dev/null
+++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenFilter.java
@@ -0,0 +1,66 @@
+package me.zhengjie.modules.security.security;
+
+import io.jsonwebtoken.ExpiredJwtException;
+import lombok.extern.slf4j.Slf4j;
+import me.zhengjie.modules.security.config.SecurityProperties;
+import me.zhengjie.modules.security.security.vo.OnlineUser;
+import me.zhengjie.modules.security.service.OnlineUserService;
+import me.zhengjie.utils.SpringContextHolder;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.GenericFilterBean;
+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;
+
+/**
+ * @author /
+ */
+@Slf4j
+public class TokenFilter extends GenericFilterBean {
+
+ private final TokenProvider tokenProvider;
+
+ TokenFilter(TokenProvider tokenProvider) {
+ this.tokenProvider = tokenProvider;
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+ throws IOException, ServletException {
+ HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
+ String token = resolveToken(httpServletRequest);
+ String requestRri = httpServletRequest.getRequestURI();
+ // 验证 token 是否存在
+ OnlineUser onlineUser = null;
+ try {
+ SecurityProperties properties = SpringContextHolder.getBean(SecurityProperties.class);
+ OnlineUserService onlineUserService = SpringContextHolder.getBean(OnlineUserService.class);
+ onlineUser = onlineUserService.getOne(properties.getOnlineKey() + token);
+ } catch (ExpiredJwtException e) {
+ log.error(e.getMessage());
+ }
+ if (onlineUser != null && StringUtils.hasText(token) && tokenProvider.validateToken(token)) {
+ Authentication authentication = tokenProvider.getAuthentication(token);
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ log.debug("set Authentication to security context for '{}', uri: {}", authentication.getName(), requestRri);
+ } else {
+ log.debug("no valid JWT token found, uri: {}", requestRri);
+ }
+
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+
+ private String resolveToken(HttpServletRequest request) {
+ SecurityProperties properties = SpringContextHolder.getBean(SecurityProperties.class);
+ String bearerToken = request.getHeader(properties.getHeader());
+ if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(properties.getTokenStartWith())) {
+ return bearerToken.substring(7);
+ }
+ return null;
+ }
+}
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenProvider.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenProvider.java
new file mode 100644
index 00000000..30e2059d
--- /dev/null
+++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenProvider.java
@@ -0,0 +1,108 @@
+package me.zhengjie.modules.security.security;
+
+import io.jsonwebtoken.*;
+import io.jsonwebtoken.io.Decoders;
+import io.jsonwebtoken.security.Keys;
+import lombok.extern.slf4j.Slf4j;
+import me.zhengjie.modules.security.config.SecurityProperties;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.stereotype.Component;
+import javax.servlet.http.HttpServletRequest;
+import java.security.Key;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.stream.Collectors;
+
+/**
+ * @author /
+ */
+@Slf4j
+@Component
+public class TokenProvider implements InitializingBean {
+
+ private final SecurityProperties properties;
+ private static final String AUTHORITIES_KEY = "auth";
+ private Key key;
+
+ public TokenProvider(SecurityProperties properties) {
+ this.properties = properties;
+ }
+
+
+ @Override
+ public void afterPropertiesSet() {
+ byte[] keyBytes = Decoders.BASE64.decode(properties.getBase64Secret());
+ this.key = Keys.hmacShaKeyFor(keyBytes);
+ }
+
+ public String createToken(Authentication authentication, boolean rememberMe) {
+ String authorities = authentication.getAuthorities().stream()
+ .map(GrantedAuthority::getAuthority)
+ .collect(Collectors.joining(","));
+
+ long now = (new Date()).getTime();
+ Date validity;
+ if (rememberMe) {
+ validity = new Date(now + properties.getTokenValidityInSecondsForRememberMe());
+ } else {
+ validity = new Date(now + properties.getTokenValidityInSeconds());
+ }
+
+ return Jwts.builder()
+ .setSubject(authentication.getName())
+ .claim(AUTHORITIES_KEY, authorities)
+ .signWith(key, SignatureAlgorithm.HS512)
+ .setExpiration(validity)
+ .compact();
+ }
+
+ Authentication getAuthentication(String token) {
+ Claims claims = Jwts.parser()
+ .setSigningKey(key)
+ .parseClaimsJws(token)
+ .getBody();
+
+ Collection extends GrantedAuthority> authorities =
+ Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
+ .map(SimpleGrantedAuthority::new)
+ .collect(Collectors.toList());
+
+ User principal = new User(claims.getSubject(), "", authorities);
+
+ return new UsernamePasswordAuthenticationToken(principal, token, authorities);
+ }
+
+ boolean validateToken(String authToken) {
+ try {
+ Jwts.parser().setSigningKey(key).parseClaimsJws(authToken);
+ return true;
+ } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
+ log.info("Invalid JWT signature.");
+ e.printStackTrace();
+ } catch (ExpiredJwtException e) {
+ log.info("Expired JWT token.");
+ e.printStackTrace();
+ } catch (UnsupportedJwtException e) {
+ log.info("Unsupported JWT token.");
+ e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ log.info("JWT token compact of handler are invalid.");
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ public String getToken(HttpServletRequest request){
+ final String requestHeader = request.getHeader(properties.getHeader());
+ if (requestHeader != null && requestHeader.startsWith(properties.getTokenStartWith())) {
+ return requestHeader.substring(7);
+ }
+ return null;
+ }
+}
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/AuthUser.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/vo/AuthUser.java
similarity index 84%
rename from eladmin-system/src/main/java/me/zhengjie/modules/security/security/AuthUser.java
rename to eladmin-system/src/main/java/me/zhengjie/modules/security/security/vo/AuthUser.java
index 8c3a9608..4c273fb7 100644
--- a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/AuthUser.java
+++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/vo/AuthUser.java
@@ -1,4 +1,4 @@
-package me.zhengjie.modules.security.security;
+package me.zhengjie.modules.security.security.vo;
import lombok.Getter;
import lombok.Setter;
@@ -19,6 +19,8 @@ public class AuthUser {
@NotBlank
private String password;
+ private Boolean rememberMe;
+
private String code;
private String uuid = "";
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtUser.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/vo/JwtUser.java
similarity index 96%
rename from eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtUser.java
rename to eladmin-system/src/main/java/me/zhengjie/modules/security/security/vo/JwtUser.java
index bc39e9a1..48c27567 100644
--- a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtUser.java
+++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/vo/JwtUser.java
@@ -1,4 +1,4 @@
-package me.zhengjie.modules.security.security;
+package me.zhengjie.modules.security.security.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/OnlineUser.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/vo/OnlineUser.java
similarity index 89%
rename from eladmin-system/src/main/java/me/zhengjie/modules/security/security/OnlineUser.java
rename to eladmin-system/src/main/java/me/zhengjie/modules/security/security/vo/OnlineUser.java
index d0bb036c..e1097890 100644
--- a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/OnlineUser.java
+++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/vo/OnlineUser.java
@@ -1,4 +1,4 @@
-package me.zhengjie.modules.security.security;
+package me.zhengjie.modules.security.security.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/JwtPermissionServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/JwtPermissionServiceImpl.java
deleted file mode 100644
index a8df0bd3..00000000
--- a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/JwtPermissionServiceImpl.java
+++ /dev/null
@@ -1,50 +0,0 @@
-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 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.Set;
-import java.util.stream.Collectors;
-
-/**
- * @author Zheng Jie
- */
-@Service
-@CacheConfig(cacheNames = "role")
-public class JwtPermissionServiceImpl {
-
- private final RoleRepository roleRepository;
-
- public JwtPermissionServiceImpl(RoleRepository roleRepository) {
- this.roleRepository = roleRepository;
- }
-
- /**
- * key的名称如有修改,请同步修改 UserServiceImpl 中的 update 方法
- * @param user 用户信息
- * @return Collection
- */
- @Cacheable(key = "'loadPermissionByUser:' + #p0.username")
- public Collection mapToGrantedAuthorities(UserDto user) {
-
- System.out.println("--------------------loadPermissionByUser:" + user.getUsername() + "---------------------");
-
- Set roles = roleRepository.findByUsers_Id(user.getId());
- Set 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());
- }
-}
diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/OnlineUserService.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/OnlineUserService.java
index f897bf8e..0a1f4943 100644
--- a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/OnlineUserService.java
+++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/OnlineUserService.java
@@ -1,12 +1,12 @@
package me.zhengjie.modules.security.service;
-import me.zhengjie.modules.security.security.JwtUser;
-import me.zhengjie.modules.security.security.OnlineUser;
+import me.zhengjie.modules.security.config.SecurityProperties;
+import me.zhengjie.modules.security.security.vo.JwtUser;
+import me.zhengjie.modules.security.security.vo.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;
@@ -26,18 +26,20 @@ import java.util.concurrent.TimeUnit;
@SuppressWarnings({"unchecked","all"})
public class OnlineUserService {
- @Value("${jwt.expiration}")
- private Long expiration;
-
- @Value("${jwt.online}")
- private String onlineKey;
-
+ private final SecurityProperties properties;
private final RedisTemplate redisTemplate;
- public OnlineUserService(RedisTemplate redisTemplate) {
+ public OnlineUserService(SecurityProperties properties, RedisTemplate redisTemplate) {
+ this.properties = properties;
this.redisTemplate = redisTemplate;
}
+ /**
+ * 保存在线用户信息
+ * @param jwtUser
+ * @param token
+ * @param request
+ */
public void save(JwtUser jwtUser, String token, HttpServletRequest request){
String job = jwtUser.getDept() + "/" + jwtUser.getJob();
String ip = StringUtils.getIp(request);
@@ -49,10 +51,16 @@ public class OnlineUserService {
} catch (Exception e) {
e.printStackTrace();
}
- redisTemplate.opsForValue().set(onlineKey + token, onlineUser);
- redisTemplate.expire(onlineKey + token,expiration, TimeUnit.MILLISECONDS);
+ redisTemplate.opsForValue().set(properties.getOnlineKey() + token, onlineUser);
+ redisTemplate.expire(properties.getOnlineKey() + token,properties.getTokenValidityInSeconds(), TimeUnit.MILLISECONDS);
}
+ /**
+ * 查询全部数据
+ * @param filter
+ * @param pageable
+ * @return
+ */
public Page getAll(String filter, Pageable pageable){
List onlineUsers = getAll(filter);
return new PageImpl(
@@ -61,8 +69,13 @@ public class OnlineUserService {
onlineUsers.size());
}
+ /**
+ * 查询全部数据,不分页
+ * @param filter
+ * @return
+ */
public List getAll(String filter){
- List keys = new ArrayList<>(redisTemplate.keys(onlineKey + "*"));
+ List keys = new ArrayList<>(redisTemplate.keys(properties.getOnlineKey() + "*"));
Collections.reverse(keys);
List onlineUsers = new ArrayList<>();
for (String key : keys) {
@@ -81,16 +94,31 @@ public class OnlineUserService {
return onlineUsers;
}
+ /**
+ * 踢出用户
+ * @param val
+ * @throws Exception
+ */
public void kickOut(String val) throws Exception {
- String key = onlineKey + EncryptUtils.desDecrypt(val);
+ String key = properties.getOnlineKey() + EncryptUtils.desDecrypt(val);
redisTemplate.delete(key);
}
+ /**
+ * 退出登录
+ * @param token
+ */
public void logout(String token) {
- String key = onlineKey + token;
+ String key = properties.getOnlineKey() + token;
redisTemplate.delete(key);
}
+ /**
+ * 导出
+ * @param all
+ * @param response
+ * @throws IOException
+ */
public void download(List all, HttpServletResponse response) throws IOException {
List