security 优化,密码加密方式优化采用BCryptPasswordEncoder方式:SHA-256 +随机盐+密钥对密码进行加密

pull/217/head
dqjdda 2019-11-28 20:05:38 +08:00
parent fe812f1c88
commit f0b31a357c
31 changed files with 498 additions and 473 deletions

View File

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

View File

@ -82,11 +82,4 @@ public class EncryptUtils {
}
return b2;
}
/**
*
*/
public static String encryptPassword(String password){
return DigestUtils.md5DigestAsHex(password.getBytes());
}
}

View File

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

View File

@ -13,7 +13,7 @@
<name>核心模块</name>
<properties>
<jjwt.version>0.9.1</jjwt.version>
<jjwt.version>0.10.6</jjwt.version>
</properties>
<dependencies>
@ -45,7 +45,17 @@
<!--jwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<artifactId>jjwt-api</artifactId>
<version>${jjwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jjwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>${jjwt.version}</version>
</dependency>

View File

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

View File

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

View File

@ -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 20191128
*/
@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 + " ";
}
}

View File

@ -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<String,Object> authInfo = new HashMap<String,Object>(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<String,Object> imgResult = new HashMap<String,Object>(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);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<DefaultSecurityFilterChain, HttpSecurity> {
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);
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
package me.zhengjie.modules.security.security;
package me.zhengjie.modules.security.security.vo;
import lombok.AllArgsConstructor;
import lombok.Data;

View File

@ -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<GrantedAuthority> mapToGrantedAuthorities(UserDto user) {
System.out.println("--------------------loadPermissionByUser:" + user.getUsername() + "---------------------");
Set<Role> roles = roleRepository.findByUsers_Id(user.getId());
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());
}
}

View File

@ -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<OnlineUser> getAll(String filter, Pageable pageable){
List<OnlineUser> onlineUsers = getAll(filter);
return new PageImpl<OnlineUser>(
@ -61,8 +69,13 @@ public class OnlineUserService {
onlineUsers.size());
}
/**
*
* @param filter
* @return
*/
public List<OnlineUser> getAll(String filter){
List<String> keys = new ArrayList<>(redisTemplate.keys(onlineKey + "*"));
List<String> keys = new ArrayList<>(redisTemplate.keys(properties.getOnlineKey() + "*"));
Collections.reverse(keys);
List<OnlineUser> 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<OnlineUser> all, HttpServletResponse response) throws IOException {
List<Map<String, Object>> list = new ArrayList<>();
for (OnlineUser user : all) {
@ -105,4 +133,13 @@ public class OnlineUserService {
}
FileUtil.downloadExcel(list, response);
}
/**
*
* @param key
* @return
*/
public OnlineUser getOne(String key) {
return (OnlineUser)redisTemplate.opsForValue().get(key);
}
}

View File

@ -1,7 +1,8 @@
package me.zhengjie.modules.security.service;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.security.security.JwtUser;
import me.zhengjie.modules.security.security.vo.JwtUser;
import me.zhengjie.modules.system.service.RoleService;
import me.zhengjie.modules.system.service.UserService;
import me.zhengjie.modules.system.service.dto.*;
import org.springframework.security.core.userdetails.UserDetails;
@ -15,31 +16,33 @@ import java.util.Optional;
* @author Zheng Jie
* @date 2018-11-22
*/
@Service
@Service("userDetailsService")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class JwtUserDetailsServiceImpl implements UserDetailsService {
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserService userService;
private final JwtPermissionServiceImpl permissionService;
private final RoleService roleService;
public JwtUserDetailsServiceImpl(UserService userService, JwtPermissionServiceImpl permissionService) {
public UserDetailsServiceImpl(UserService userService, RoleService roleService) {
this.userService = userService;
this.permissionService = permissionService;
this.roleService = roleService;
}
@Override
public UserDetails loadUserByUsername(String username){
UserDto user = userService.findByName(username);
if (user == null) {
throw new BadRequestException("账号不存在");
} else {
if (!user.getEnabled()) {
throw new BadRequestException("账号未激活");
}
return createJwtUser(user);
}
}
public UserDetails createJwtUser(UserDto user) {
private UserDetails createJwtUser(UserDto user) {
return new JwtUser(
user.getId(),
user.getUsername(),
@ -49,7 +52,7 @@ public class JwtUserDetailsServiceImpl implements UserDetailsService {
user.getPhone(),
Optional.ofNullable(user.getDept()).map(DeptSmallDto::getName).orElse(null),
Optional.ofNullable(user.getJob()).map(JobSmallDto::getName).orElse(null),
permissionService.mapToGrantedAuthorities(user),
roleService.mapToGrantedAuthorities(user),
user.getEnabled(),
user.getCreateTime(),
user.getLastPasswordResetTime()

View File

@ -1,134 +0,0 @@
package me.zhengjie.modules.security.utils;
import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.DefaultClock;
import me.zhengjie.modules.security.security.JwtUser;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
/**
* @author /
*/
@Component
public class JwtTokenUtil implements Serializable {
private static final long serialVersionUID = -3301605591108950415L;
private Clock clock = DefaultClock.INSTANCE;
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
@Value("${jwt.header}")
private String tokenHeader;
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
private Date getIssuedAtDateFromToken(String token) {
return getClaimFromToken(token, Claims::getIssuedAt);
}
private Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
private <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(clock.now());
}
private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
return (lastPasswordReset != null && created.before(lastPasswordReset));
}
private Boolean ignoreTokenExpiration(String token) {
// here you specify tokens, for that the expiration is ignored
return false;
}
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>(16);
return doGenerateToken(claims, userDetails.getUsername());
}
private String doGenerateToken(Map<String, Object> claims, String subject) {
final Date createdDate = clock.now();
final Date expirationDate = calculateExpirationDate(createdDate);
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(createdDate)
.setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
final Date created = getIssuedAtDateFromToken(token);
return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset)
&& (!isTokenExpired(token) || ignoreTokenExpiration(token));
}
public String refreshToken(String token) {
final Date createdDate = clock.now();
final Date expirationDate = calculateExpirationDate(createdDate);
final Claims claims = getAllClaimsFromToken(token);
claims.setIssuedAt(createdDate);
claims.setExpiration(expirationDate);
return Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public String getToken(HttpServletRequest request){
final String requestHeader = request.getHeader(tokenHeader);
String startsWith = "Bearer ";
if (requestHeader != null && requestHeader.startsWith(startsWith)) {
return requestHeader.substring(7);
}
return null;
}
public Boolean validateToken(String token, UserDetails userDetails) {
JwtUser user = (JwtUser) userDetails;
final Date created = getIssuedAtDateFromToken(token);
// final Date expiration = getExpirationDateFromToken(token);
// 如果token存在且token创建日期 > 最后修改密码的日期 则代表token有效
return (!isTokenExpired(token)
&& !isCreatedBeforeLastPasswordReset(created, user.getLastPasswordResetDate())
);
}
private Date calculateExpirationDate(Date createdDate) {
return new Date(createdDate.getTime() + expiration);
}
}

View File

@ -5,6 +5,7 @@ import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* @author Zheng Jie
@ -36,9 +37,9 @@ public interface MenuRepository extends JpaRepository<Menu, Long>, JpaSpecificat
/**
* ID
* @param id roleID
* @param roleIds roleIDs
* @param type
* @return /
*/
LinkedHashSet<Menu> findByRoles_IdAndTypeIsNotInOrderBySortAsc(Long id, Integer type);
LinkedHashSet<Menu> findByRoles_IdInAndTypeNotOrderBySortAsc(Set<Long> roleIds, int type);
}

View File

@ -7,8 +7,10 @@ import me.zhengjie.aop.log.Log;
import me.zhengjie.modules.system.domain.Role;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.system.service.RoleService;
import me.zhengjie.modules.system.service.UserService;
import me.zhengjie.modules.system.service.dto.RoleQueryCriteria;
import me.zhengjie.modules.system.service.dto.RoleSmallDto;
import me.zhengjie.modules.system.service.dto.UserDto;
import me.zhengjie.utils.SecurityUtils;
import me.zhengjie.utils.ThrowableUtil;
import org.springframework.data.domain.Pageable;
@ -36,11 +38,13 @@ import java.util.stream.Collectors;
public class RoleController {
private final RoleService roleService;
private final UserService userService;
private static final String ENTITY_NAME = "role";
public RoleController(RoleService roleService) {
public RoleController(RoleService roleService, UserService userService) {
this.roleService = roleService;
this.userService = userService;
}
@ApiOperation("获取单个role")
@ -76,7 +80,8 @@ public class RoleController {
@ApiOperation("获取用户级别")
@GetMapping(value = "/level")
public ResponseEntity getLevel(){
List<Integer> levels = roleService.findByUsersId(SecurityUtils.getUserId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList());
UserDto user = userService.findByName(SecurityUtils.getUsername());
List<Integer> levels = roleService.findByUsersId(user.getId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList());
return new ResponseEntity<>(Dict.create().set("level", Collections.min(levels)),HttpStatus.OK);
}

View File

@ -11,6 +11,7 @@ import me.zhengjie.modules.system.domain.vo.UserPassVo;
import me.zhengjie.modules.system.service.DeptService;
import me.zhengjie.modules.system.service.RoleService;
import me.zhengjie.modules.system.service.dto.RoleSmallDto;
import me.zhengjie.modules.system.service.dto.UserDto;
import me.zhengjie.modules.system.service.dto.UserQueryCriteria;
import me.zhengjie.service.VerificationCodeService;
import me.zhengjie.utils.*;
@ -20,6 +21,8 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
@ -39,17 +42,15 @@ import java.util.stream.Collectors;
@RequestMapping("/api/users")
public class UserController {
private final PasswordEncoder passwordEncoder;
private final UserService userService;
private final DataScope dataScope;
private final DeptService deptService;
private final RoleService roleService;
private final VerificationCodeService verificationCodeService;
public UserController(UserService userService, DataScope dataScope, DeptService deptService, RoleService roleService, VerificationCodeService verificationCodeService) {
public UserController(PasswordEncoder passwordEncoder, UserService userService, DataScope dataScope, DeptService deptService, RoleService roleService, VerificationCodeService verificationCodeService) {
this.passwordEncoder = passwordEncoder;
this.userService = userService;
this.dataScope = dataScope;
this.deptService = deptService;
@ -123,7 +124,8 @@ public class UserController {
@DeleteMapping(value = "/{id}")
@PreAuthorize("@el.check('user:del')")
public ResponseEntity delete(@PathVariable Long id){
Integer currentLevel = Collections.min(roleService.findByUsersId(SecurityUtils.getUserId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
UserDto user = userService.findByName(SecurityUtils.getUsername());
Integer currentLevel = Collections.min(roleService.findByUsersId(user.getId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
Integer optLevel = Collections.min(roleService.findByUsersId(id).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
if (currentLevel > optLevel) {
@ -135,15 +137,15 @@ public class UserController {
@ApiOperation("修改密码")
@PostMapping(value = "/updatePass")
public ResponseEntity updatePass(@RequestBody UserPassVo user){
UserDetails userDetails = SecurityUtils.getUserDetails();
if(!userDetails.getPassword().equals(EncryptUtils.encryptPassword(user.getOldPass()))){
public ResponseEntity updatePass(@RequestBody UserPassVo passVo){
UserDto user = userService.findByName(SecurityUtils.getUsername());
if(!passwordEncoder.matches(passVo.getOldPass(), user.getPassword())){
throw new BadRequestException("修改失败,旧密码错误");
}
if(userDetails.getPassword().equals(EncryptUtils.encryptPassword(user.getNewPass()))){
if(passwordEncoder.matches(passVo.getNewPass(), user.getPassword())){
throw new BadRequestException("新密码不能与旧密码相同");
}
userService.updatePass(userDetails.getUsername(),EncryptUtils.encryptPassword(user.getNewPass()));
userService.updatePass(user.getUsername(),passwordEncoder.encode(passVo.getNewPass()));
return new ResponseEntity(HttpStatus.OK);
}
@ -158,13 +160,13 @@ public class UserController {
@ApiOperation("修改邮箱")
@PostMapping(value = "/updateEmail/{code}")
public ResponseEntity updateEmail(@PathVariable String code,@RequestBody User user){
UserDetails userDetails = SecurityUtils.getUserDetails();
if(!userDetails.getPassword().equals(EncryptUtils.encryptPassword(user.getPassword()))){
UserDto userDto = userService.findByName(SecurityUtils.getUsername());
if(!passwordEncoder.matches(user.getPassword(), userDto.getPassword())){
throw new BadRequestException("密码错误");
}
VerificationCode verificationCode = new VerificationCode(code, ElAdminConstant.RESET_MAIL,"email",user.getEmail());
verificationCodeService.validated(verificationCode);
userService.updateEmail(userDetails.getUsername(),user.getEmail());
userService.updateEmail(userDto.getUsername(),user.getEmail());
return new ResponseEntity(HttpStatus.OK);
}
@ -173,7 +175,8 @@ public class UserController {
* @param resources /
*/
private void checkLevel(User resources) {
Integer currentLevel = Collections.min(roleService.findByUsersId(SecurityUtils.getUserId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
UserDto user = userService.findByName(SecurityUtils.getUsername());
Integer currentLevel = Collections.min(roleService.findByUsersId(user.getId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
Integer optLevel = roleService.findByRoles(resources.getRoles());
if (currentLevel > optLevel) {
throw new BadRequestException("角色权限不足");

View File

@ -4,10 +4,14 @@ import me.zhengjie.modules.system.domain.Role;
import me.zhengjie.modules.system.service.dto.RoleDto;
import me.zhengjie.modules.system.service.dto.RoleQueryCriteria;
import me.zhengjie.modules.system.service.dto.RoleSmallDto;
import me.zhengjie.modules.system.service.dto.UserDto;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Pageable;
import org.springframework.security.core.GrantedAuthority;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@ -99,4 +103,11 @@ public interface RoleService {
* @throws IOException /
*/
void download(List<RoleDto> queryAll, HttpServletResponse response) throws IOException;
/**
*
* @param user
* @return
*/
Collection<GrantedAuthority> mapToGrantedAuthorities(UserDto user);
}

View File

@ -66,11 +66,8 @@ public class MenuServiceImpl implements MenuService {
@Override
public List<MenuDto> findByRoles(List<RoleSmallDto> roles) {
Set<Menu> menus = new LinkedHashSet<>();
for (RoleSmallDto role : roles) {
List<Menu> menus1 = new ArrayList<>(menuRepository.findByRoles_IdAndTypeIsNotInOrderBySortAsc(role.getId(), 2));
menus.addAll(menus1);
}
Set<Long> roleIds = roles.stream().map(RoleSmallDto::getId).collect(Collectors.toSet());
LinkedHashSet<Menu> menus = menuRepository.findByRoles_IdInAndTypeNotOrderBySortAsc(roleIds, 2);
return menus.stream().map(menuMapper::toDto).collect(Collectors.toList());
}

View File

@ -1,5 +1,6 @@
package me.zhengjie.modules.system.service.impl;
import me.zhengjie.modules.system.domain.Menu;
import me.zhengjie.modules.system.domain.Role;
import me.zhengjie.exception.EntityExistException;
import me.zhengjie.modules.system.repository.RoleRepository;
@ -7,17 +8,17 @@ import me.zhengjie.modules.system.service.RoleService;
import me.zhengjie.modules.system.service.dto.RoleDto;
import me.zhengjie.modules.system.service.dto.RoleQueryCriteria;
import me.zhengjie.modules.system.service.dto.RoleSmallDto;
import me.zhengjie.modules.system.service.dto.UserDto;
import me.zhengjie.modules.system.service.mapper.RoleMapper;
import me.zhengjie.modules.system.service.mapper.RoleSmallMapper;
import me.zhengjie.utils.FileUtil;
import me.zhengjie.utils.PageUtil;
import me.zhengjie.utils.QueryHelp;
import me.zhengjie.utils.ValidationUtil;
import me.zhengjie.utils.*;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@ -144,6 +145,20 @@ public class RoleServiceImpl implements RoleService {
return Collections.min(roleDtos.stream().map(RoleDto::getLevel).collect(Collectors.toList()));
}
@Override
@Cacheable(key = "'loadPermissionByUser:' + #p0.username")
public Collection<GrantedAuthority> mapToGrantedAuthorities(UserDto user) {
Set<Role> roles = roleRepository.findByUsers_Id(user.getId());
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(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
@Override
public void download(List<RoleDto> roles, HttpServletResponse response) throws IOException {
List<Map<String, Object>> list = new ArrayList<>();

View File

@ -2,7 +2,7 @@
spring:
datasource:
druid:
type: com.alibaba.druid.pool.DruidDataSource
db-type: com.alibaba.druid.pool.DruidDataSource
driverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
url: jdbc:log4jdbc:mysql://localhost:3306/eladmin?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
username: root
@ -44,13 +44,18 @@ spring:
#jwt
jwt:
header: Authorization
secret: mySecret
# token 过期时间/毫秒6小时 1小时 = 3600000 毫秒
expiration: 21600000
# 令牌前缀,主要最后留个空格
token-start-with: Bearer
# 必须使用最少88位的Base64对该令牌进行编码
base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI=
# 令牌过期时间 此处单位/秒 默认4小时
token-validity-in-seconds: 14400000
# 记住我模式下的令牌过期时间 此处单位/毫秒 默认1天
token-validity-in-seconds-for-remember-me: 86400000
# 在线用户key
online: online-token
online-key: online-token
# 验证码
codeKey: code-key
code-key: code-key
#是否允许生成代码生产环境设置为false
generator:

View File

@ -2,7 +2,7 @@
spring:
datasource:
druid:
type: com.alibaba.druid.pool.DruidDataSource
db-type: com.alibaba.druid.pool.DruidDataSource
driverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
url: jdbc:log4jdbc:mysql://localhost:3306/eladmin?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
username: root
@ -46,13 +46,18 @@ spring:
#jwt
jwt:
header: Authorization
secret: mySecret
# token 过期时间 2个小时
expiration: 7200000
# 令牌前缀,主要最后留个空格
token-start-with: Bearer
# 必须使用最少88位的Base64对该令牌进行编码
base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI=
# 令牌过期时间 此处单位/秒 默认4小时
token-validity-in-seconds: 14400000
# 记住我模式下的令牌过期时间 此处单位/毫秒 默认1天
token-validity-in-seconds-for-remember-me: 86400000
# 在线用户key
online: online-token
online-key: online-token
# 验证码
codeKey: code-key
code-key: code-key
#是否允许生成代码生产环境设置为false
generator: