From 94004397003116c73027a62e43c0d8dc1fde0664 Mon Sep 17 00:00:00 2001 From: "chen.ma" Date: Fri, 12 Nov 2021 23:29:53 +0800 Subject: [PATCH] =?UTF-8?q?Feature:=20JWT=20+=20Spring=20Security=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E7=94=A8=E6=88=B7=E6=9D=83=E9=99=90=E8=AE=A4?= =?UTF-8?q?=E8=AF=81.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- auth/pom.xml | 16 +++ .../auth/config/GlobalSecurityConfig.java | 81 ++++++++++++ .../threadpool/auth/constant/Constants.java | 15 +++ .../auth/filter/JWTAuthenticationFilter.java | 93 ++++++++++++++ .../auth/filter/JWTAuthorizationFilter.java | 83 ++++++++++++ .../threadpool/auth/model/UserInfo.java | 5 + .../auth/model/biz/user/JwtUser.java | 58 +++++++++ .../auth/model/biz/user/LoginUser.java | 29 +++++ .../model/biz/user/UserQueryPageReqDTO.java | 7 +- .../auth/model/biz/user/UserReqDTO.java | 30 +++++ .../auth/model/biz/user/UserRespDTO.java | 4 +- .../threadpool/auth/service/UserService.java | 17 ++- .../service/impl/UserDetailsServiceImpl.java | 42 ++++++ .../auth/service/impl/UserServiceImpl.java | 36 +++--- .../threadpool/auth/toolkit/JwtTokenUtil.java | 120 ++++++++++++++++++ .../threadpool/auth/toolkit/ReturnT.java | 40 ++++++ pom.xml | 5 +- 17 files changed, 648 insertions(+), 33 deletions(-) create mode 100644 auth/src/main/java/com/github/dynamic/threadpool/auth/config/GlobalSecurityConfig.java create mode 100644 auth/src/main/java/com/github/dynamic/threadpool/auth/constant/Constants.java create mode 100644 auth/src/main/java/com/github/dynamic/threadpool/auth/filter/JWTAuthenticationFilter.java create mode 100644 auth/src/main/java/com/github/dynamic/threadpool/auth/filter/JWTAuthorizationFilter.java create mode 100644 auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/JwtUser.java create mode 100644 auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/LoginUser.java create mode 100644 auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/UserReqDTO.java create mode 100644 auth/src/main/java/com/github/dynamic/threadpool/auth/service/impl/UserDetailsServiceImpl.java create mode 100644 auth/src/main/java/com/github/dynamic/threadpool/auth/toolkit/JwtTokenUtil.java create mode 100644 auth/src/main/java/com/github/dynamic/threadpool/auth/toolkit/ReturnT.java diff --git a/auth/pom.xml b/auth/pom.xml index b5e18b3a..f87bb8ce 100644 --- a/auth/pom.xml +++ b/auth/pom.xml @@ -53,6 +53,22 @@ cn.hutool hutool-all + + + org.springframework.boot + spring-boot-starter-security + + + + io.jsonwebtoken + jjwt + ${jjwt.version} + + + + io.github.acmenlt + common + diff --git a/auth/src/main/java/com/github/dynamic/threadpool/auth/config/GlobalSecurityConfig.java b/auth/src/main/java/com/github/dynamic/threadpool/auth/config/GlobalSecurityConfig.java new file mode 100644 index 00000000..bff298ff --- /dev/null +++ b/auth/src/main/java/com/github/dynamic/threadpool/auth/config/GlobalSecurityConfig.java @@ -0,0 +1,81 @@ +package com.github.dynamic.threadpool.auth.config; + +import com.github.dynamic.threadpool.auth.constant.Constants; +import com.github.dynamic.threadpool.auth.filter.JWTAuthenticationFilter; +import com.github.dynamic.threadpool.auth.filter.JWTAuthorizationFilter; +import com.github.dynamic.threadpool.auth.service.impl.UserDetailsServiceImpl; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +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.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import javax.annotation.Resource; + +/** + * 安全配置. + * + * @author chen.ma + * @date 2021/11/9 21:10 + */ +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class GlobalSecurityConfig extends WebSecurityConfigurerAdapter { + + @Resource + private UserDetailsService userDetailsService; + + @Bean + public UserDetailsService customUserService() { + return new UserDetailsServiceImpl(); + } + + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.addAllowedMethod(Constants.SPLIT_STAR); + config.applyPermitDefaultValues(); + source.registerCorsConfiguration("/**", config); + return source; + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.cors().and().csrf().disable() + .authorizeRequests() + .antMatchers("/static/**", "/index.html", "/favicon.ico", "/avatar.jpg").permitAll() + .antMatchers("/doc.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs").anonymous() + .anyRequest().authenticated() + .and() + .addFilter(new JWTAuthenticationFilter(authenticationManager())) + .addFilter(new JWTAuthorizationFilter(authenticationManager())) + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); + } + + @Override + public void configure(WebSecurity web) throws Exception { + web.ignoring().antMatchers("/v1/cs/apps/renew/**", "/v1/cs/apps/register/**", "/v1/cs/configs/**", "/v1/cs/listener/**"); + } + +} diff --git a/auth/src/main/java/com/github/dynamic/threadpool/auth/constant/Constants.java b/auth/src/main/java/com/github/dynamic/threadpool/auth/constant/Constants.java new file mode 100644 index 00000000..4b7e98f5 --- /dev/null +++ b/auth/src/main/java/com/github/dynamic/threadpool/auth/constant/Constants.java @@ -0,0 +1,15 @@ +package com.github.dynamic.threadpool.auth.constant; + +/** + * Constants. + * + * @author chen.ma + * @date 2021/11/9 22:24 + */ +public class Constants { + + public static final String SPLIT_STAR = "*"; + + public static final String SPLIT_COMMA = ","; + +} diff --git a/auth/src/main/java/com/github/dynamic/threadpool/auth/filter/JWTAuthenticationFilter.java b/auth/src/main/java/com/github/dynamic/threadpool/auth/filter/JWTAuthenticationFilter.java new file mode 100644 index 00000000..e8c9f38f --- /dev/null +++ b/auth/src/main/java/com/github/dynamic/threadpool/auth/filter/JWTAuthenticationFilter.java @@ -0,0 +1,93 @@ +package com.github.dynamic.threadpool.auth.filter; + +import cn.hutool.json.JSONUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dynamic.threadpool.auth.model.biz.user.JwtUser; +import com.github.dynamic.threadpool.auth.model.biz.user.LoginUser; +import com.github.dynamic.threadpool.auth.toolkit.JwtTokenUtil; +import com.github.dynamic.threadpool.auth.toolkit.ReturnT; +import com.github.dynamic.threadpool.common.web.base.Results; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import static com.github.dynamic.threadpool.auth.constant.Constants.SPLIT_COMMA; + +/** + * JWT authentication filter. + * + * @author chen.ma + * @date 2021/11/9 22:21 + */ +@Slf4j +public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter { + + private AuthenticationManager authenticationManager; + + private ThreadLocal rememberMe = new ThreadLocal(); + + public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + super.setFilterProcessesUrl("/v1/cs/auth/login"); + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, + HttpServletResponse response) throws AuthenticationException { + // 从输入流中获取到登录的信息 + try { + LoginUser loginUser = new ObjectMapper().readValue(request.getInputStream(), LoginUser.class); + rememberMe.set(loginUser.getRememberMe()); + return authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword(), new ArrayList<>()) + ); + } catch (IOException e) { + logger.error("attemptAuthentication error :{}", e); + return null; + } + } + + @Override + protected void successfulAuthentication(HttpServletRequest request, + HttpServletResponse response, + FilterChain chain, + Authentication authResult) throws IOException { + JwtUser jwtUser = (JwtUser) authResult.getPrincipal(); + boolean isRemember = rememberMe.get() == 1; + + String role = ""; + Collection authorities = jwtUser.getAuthorities(); + for (GrantedAuthority authority : authorities) { + role = authority.getAuthority(); + } + + String token = JwtTokenUtil.createToken(jwtUser.getId(), jwtUser.getUsername(), role, isRemember); + response.setHeader("token", JwtTokenUtil.TOKEN_PREFIX + token); + response.setCharacterEncoding("UTF-8"); + Map maps = new HashMap<>(); + maps.put("data", JwtTokenUtil.TOKEN_PREFIX + token); + maps.put("roles", role.split(SPLIT_COMMA)); + response.getWriter().write(JSONUtil.toJsonStr(Results.success(maps))); + } + + @Override + protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { + response.setCharacterEncoding("UTF-8"); + response.getWriter().write(JSONUtil.toJsonStr(new ReturnT(-1, "Server Error"))); + } + +} diff --git a/auth/src/main/java/com/github/dynamic/threadpool/auth/filter/JWTAuthorizationFilter.java b/auth/src/main/java/com/github/dynamic/threadpool/auth/filter/JWTAuthorizationFilter.java new file mode 100644 index 00000000..0e927a96 --- /dev/null +++ b/auth/src/main/java/com/github/dynamic/threadpool/auth/filter/JWTAuthorizationFilter.java @@ -0,0 +1,83 @@ +package com.github.dynamic.threadpool.auth.filter; + +import com.alibaba.fastjson.JSON; +import com.github.dynamic.threadpool.auth.toolkit.JwtTokenUtil; +import com.github.dynamic.threadpool.common.web.base.Results; +import com.github.dynamic.threadpool.common.web.exception.ServiceException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Collections; + +/** + * JWT authorization filter. + * + * @author chen.ma + * @date 2021/11/9 22:21 + */ +@Slf4j +public class JWTAuthorizationFilter extends BasicAuthenticationFilter { + + public JWTAuthorizationFilter(AuthenticationManager authenticationManager) { + super(authenticationManager); + } + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain chain) throws IOException, ServletException { + + String tokenHeader = request.getHeader(JwtTokenUtil.TOKEN_HEADER); + // 如果请求头中没有 Authorization 信息则直接放行 + if (tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtil.TOKEN_PREFIX)) { + chain.doFilter(request, response); + return; + } + // 如果请求头中有 Token, 则进行解析, 并且设置认证信息 + try { + SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader)); + } catch (Exception ex) { + // 返回 Json 形式的错误信息 + response.setCharacterEncoding("UTF-8"); + response.setContentType("application/json; charset=utf-8"); + response.getWriter().write(JSON.toJSONString(Results.failure("-1", ex.getMessage()))); + response.getWriter().flush(); + return; + } + super.doFilterInternal(request, response, chain); + } + + /** + * Token 中获取用户信息并新建一个 Token. + * + * @param tokenHeader + * @return + */ + private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader) { + String token = tokenHeader.replace(JwtTokenUtil.TOKEN_PREFIX, ""); + boolean expiration = JwtTokenUtil.isExpiration(token); + if (expiration) { + throw new ServiceException("登录时间过长,请退出重新登录"); + } + + String username = JwtTokenUtil.getUsername(token); + String role = JwtTokenUtil.getUserRole(token); + if (username != null) { + return new UsernamePasswordAuthenticationToken(username, null, + Collections.singleton(new SimpleGrantedAuthority(role)) + ); + } + + return null; + } + +} diff --git a/auth/src/main/java/com/github/dynamic/threadpool/auth/model/UserInfo.java b/auth/src/main/java/com/github/dynamic/threadpool/auth/model/UserInfo.java index a472849d..819099d6 100644 --- a/auth/src/main/java/com/github/dynamic/threadpool/auth/model/UserInfo.java +++ b/auth/src/main/java/com/github/dynamic/threadpool/auth/model/UserInfo.java @@ -31,6 +31,11 @@ public class UserInfo { */ private String password; + /** + * role + */ + private String role; + /** * gmtCreate */ diff --git a/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/JwtUser.java b/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/JwtUser.java new file mode 100644 index 00000000..e148152d --- /dev/null +++ b/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/JwtUser.java @@ -0,0 +1,58 @@ +package com.github.dynamic.threadpool.auth.model.biz.user; + +import lombok.Data; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; + +/** + * Jwt user. + * + * @author chen.ma + * @date 2021/11/9 22:34 + */ +@Data +public class JwtUser implements UserDetails { + + /** + * id + */ + private Long id; + + /** + * userName + */ + private String username; + + /** + * password + */ + private String password; + + /** + * authorities + */ + private Collection authorities; + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } + +} diff --git a/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/LoginUser.java b/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/LoginUser.java new file mode 100644 index 00000000..107ccb24 --- /dev/null +++ b/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/LoginUser.java @@ -0,0 +1,29 @@ +package com.github.dynamic.threadpool.auth.model.biz.user; + +import lombok.Data; + +/** + * Login user. + * + * @author chen.ma + * @date 2021/11/9 22:41 + */ +@Data +public class LoginUser { + + /** + * username + */ + private String username; + + /** + * password + */ + private String password; + + /** + * rememberMe + */ + private Integer rememberMe; + +} diff --git a/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/UserQueryPageReqDTO.java b/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/UserQueryPageReqDTO.java index 50c708bf..babc9d05 100644 --- a/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/UserQueryPageReqDTO.java +++ b/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/UserQueryPageReqDTO.java @@ -12,8 +12,9 @@ import lombok.Data; @Data public class UserQueryPageReqDTO extends Page { - public UserQueryPageReqDTO(long current, long size) { - super(current, size); - } + /** + * userName + */ + private String userName; } diff --git a/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/UserReqDTO.java b/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/UserReqDTO.java new file mode 100644 index 00000000..00d7dd82 --- /dev/null +++ b/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/UserReqDTO.java @@ -0,0 +1,30 @@ +package com.github.dynamic.threadpool.auth.model.biz.user; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.Data; + +/** + * User req dto. + * + * @author chen.ma + * @date 2021/11/11 20:30 + */ +@Data +public class UserReqDTO extends Page { + + /** + * userName + */ + private String userName; + + /** + * password + */ + private String password; + + /** + * role + */ + private String role; + +} diff --git a/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/UserRespDTO.java b/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/UserRespDTO.java index 790cdadd..921085f0 100644 --- a/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/UserRespDTO.java +++ b/auth/src/main/java/com/github/dynamic/threadpool/auth/model/biz/user/UserRespDTO.java @@ -20,9 +20,9 @@ public class UserRespDTO { private String userName; /** - * password + * role */ - private String password; + private String role; /** * gmtCreate diff --git a/auth/src/main/java/com/github/dynamic/threadpool/auth/service/UserService.java b/auth/src/main/java/com/github/dynamic/threadpool/auth/service/UserService.java index d98a294b..aaf4ff40 100644 --- a/auth/src/main/java/com/github/dynamic/threadpool/auth/service/UserService.java +++ b/auth/src/main/java/com/github/dynamic/threadpool/auth/service/UserService.java @@ -1,6 +1,8 @@ package com.github.dynamic.threadpool.auth.service; import com.baomidou.mybatisplus.core.metadata.IPage; +import com.github.dynamic.threadpool.auth.model.biz.user.UserQueryPageReqDTO; +import com.github.dynamic.threadpool.auth.model.biz.user.UserReqDTO; import com.github.dynamic.threadpool.auth.model.biz.user.UserRespDTO; import java.util.List; @@ -16,27 +18,24 @@ public interface UserService { /** * 分页查询用户列表. * - * @param pageNo - * @param pageSize + * @param reqDTO * @return */ - IPage listUser(int pageNo, int pageSize); + IPage listUser(UserQueryPageReqDTO reqDTO); /** * 新增用户. * - * @param userName - * @param password + * @param reqDTO */ - void addUser(String userName, String password); + void addUser(UserReqDTO reqDTO); /** * 修改用户. * - * @param userName - * @param password + * @param reqDTO */ - void updateUser(String userName, String password); + void updateUser(UserReqDTO reqDTO); /** * 删除用户. diff --git a/auth/src/main/java/com/github/dynamic/threadpool/auth/service/impl/UserDetailsServiceImpl.java b/auth/src/main/java/com/github/dynamic/threadpool/auth/service/impl/UserDetailsServiceImpl.java new file mode 100644 index 00000000..37ade52d --- /dev/null +++ b/auth/src/main/java/com/github/dynamic/threadpool/auth/service/impl/UserDetailsServiceImpl.java @@ -0,0 +1,42 @@ +package com.github.dynamic.threadpool.auth.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.github.dynamic.threadpool.auth.mapper.UserMapper; +import com.github.dynamic.threadpool.auth.model.UserInfo; +import com.github.dynamic.threadpool.auth.model.biz.user.JwtUser; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +import javax.annotation.Resource; +import java.util.Collections; +import java.util.Set; + +/** + * User details service impl. + * + * @author chen.ma + * @date 2021/11/9 22:26 + */ +public class UserDetailsServiceImpl implements UserDetailsService { + + @Resource + private UserMapper userMapper; + + @Override + public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { + UserInfo userInfo = userMapper.selectOne(Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getUserName, userName)); + + JwtUser jwtUser = new JwtUser(); + jwtUser.setId(userInfo.getId()); + jwtUser.setUsername(userName); + jwtUser.setPassword(userInfo.getPassword()); + + Set authorities = Collections.singleton(new SimpleGrantedAuthority(userInfo.getRole() + "")); + jwtUser.setAuthorities(authorities); + + return jwtUser; + } + +} diff --git a/auth/src/main/java/com/github/dynamic/threadpool/auth/service/impl/UserServiceImpl.java b/auth/src/main/java/com/github/dynamic/threadpool/auth/service/impl/UserServiceImpl.java index f4753c3c..79708a17 100644 --- a/auth/src/main/java/com/github/dynamic/threadpool/auth/service/impl/UserServiceImpl.java +++ b/auth/src/main/java/com/github/dynamic/threadpool/auth/service/impl/UserServiceImpl.java @@ -1,7 +1,7 @@ package com.github.dynamic.threadpool.auth.service.impl; import cn.hutool.core.bean.BeanUtil; -import cn.hutool.crypto.SecureUtil; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; @@ -9,10 +9,12 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.github.dynamic.threadpool.auth.mapper.UserMapper; import com.github.dynamic.threadpool.auth.model.UserInfo; import com.github.dynamic.threadpool.auth.model.biz.user.UserQueryPageReqDTO; +import com.github.dynamic.threadpool.auth.model.biz.user.UserReqDTO; import com.github.dynamic.threadpool.auth.model.biz.user.UserRespDTO; import com.github.dynamic.threadpool.auth.service.RoleService; import com.github.dynamic.threadpool.auth.service.UserService; import lombok.AllArgsConstructor; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import java.util.List; @@ -32,39 +34,39 @@ public class UserServiceImpl implements UserService { private final RoleService roleService; + private final BCryptPasswordEncoder bCryptPasswordEncoder; + @Override - public IPage listUser(int pageNo, int pageSize) { - UserQueryPageReqDTO queryPage = new UserQueryPageReqDTO(pageNo, pageSize); - IPage selectPage = userMapper.selectPage(queryPage, null); + public IPage listUser(UserQueryPageReqDTO reqDTO) { + IPage selectPage = userMapper.selectPage(reqDTO, null); return selectPage.convert(each -> BeanUtil.toBean(each, UserRespDTO.class)); } @Override - public void addUser(String userName, String password) { + public void addUser(UserReqDTO reqDTO) { LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(UserInfo.class) - .eq(UserInfo::getUserName, userName); + .eq(UserInfo::getUserName, reqDTO.getUserName()); UserInfo existUserInfo = userMapper.selectOne(queryWrapper); if (existUserInfo != null) { throw new RuntimeException("用户名重复"); } - UserInfo insertUser = new UserInfo(); - insertUser.setUserName(userName); - // TODO 暂定为 Md5 加密 - insertUser.setPassword(SecureUtil.md5(password)); + reqDTO.setPassword(bCryptPasswordEncoder.encode(reqDTO.getPassword())); + UserInfo insertUser = BeanUtil.toBean(reqDTO, UserInfo.class); userMapper.insert(insertUser); } @Override - public void updateUser(String userName, String password) { - UserInfo userInfo = new UserInfo(); - userInfo.setUserName(userName); - userInfo.setPassword(SecureUtil.md5(password)); + public void updateUser(UserReqDTO reqDTO) { + if (StrUtil.isNotBlank(reqDTO.getPassword())) { + reqDTO.setPassword(bCryptPasswordEncoder.encode(reqDTO.getPassword())); + } + UserInfo updateUser = BeanUtil.toBean(reqDTO, UserInfo.class); LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(UserInfo.class) - .eq(UserInfo::getUserName, userName); - userMapper.update(userInfo, updateWrapper); + .eq(UserInfo::getUserName, reqDTO.getUserName()); + userMapper.update(updateUser, updateWrapper); } @Override @@ -72,7 +74,7 @@ public class UserServiceImpl implements UserService { LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(UserInfo.class) .eq(UserInfo::getUserName, userName); userMapper.delete(updateWrapper); - roleService.deleteRole("", userName); + // roleService.deleteRole("", userName); } @Override diff --git a/auth/src/main/java/com/github/dynamic/threadpool/auth/toolkit/JwtTokenUtil.java b/auth/src/main/java/com/github/dynamic/threadpool/auth/toolkit/JwtTokenUtil.java new file mode 100644 index 00000000..174b3c62 --- /dev/null +++ b/auth/src/main/java/com/github/dynamic/threadpool/auth/toolkit/JwtTokenUtil.java @@ -0,0 +1,120 @@ +package com.github.dynamic.threadpool.auth.toolkit; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +import static com.github.dynamic.threadpool.auth.constant.Constants.SPLIT_COMMA; + +/** + * Jwt token util. + * + * @author chen.ma + * @date 2021/11/9 22:43 + */ +public class JwtTokenUtil { + + public static final String TOKEN_HEADER = "Authorization"; + public static final String TOKEN_PREFIX = "Bearer "; + + private static final String SECRET = "Hippo4J_admin"; + private static final String ISS = "admin"; + + /** + * 角色的 Key + */ + private static final String ROLE_CLAIMS = "rol"; + + /** + * 过期时间是 3600 秒, 既 24 小时 + */ + private static final long EXPIRATION = 86400L; + + /** + * 选择了记住我之后的过期时间为 7 天 + */ + private static final long EXPIRATION_REMEMBER = 7 * EXPIRATION; + + /** + * 创建 Token. + * + * @param id + * @param username + * @param role + * @param isRememberMe + * @return + */ + public static String createToken(Long id, String username, String role, boolean isRememberMe) { + long expiration = isRememberMe ? EXPIRATION_REMEMBER : EXPIRATION; + HashMap map = new HashMap<>(); + map.put(ROLE_CLAIMS, role); + return Jwts.builder() + .signWith(SignatureAlgorithm.HS512, SECRET) + .setClaims(map) + .setIssuer(ISS) + .setSubject(id + SPLIT_COMMA + username) + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000)) + .compact(); + } + + /** + * Token 中获取用户名. + * + * @param token + * @return + */ + public static String getUsername(String token) { + List userInfo = Arrays.asList(getTokenBody(token).getSubject().split(SPLIT_COMMA)); + return userInfo.get(1); + } + + /** + * Token 中获取用户名. + * + * @param token + * @return + */ + public static Integer getUserId(String token) { + List userInfo = Arrays.asList(getTokenBody(token).getSubject().split(SPLIT_COMMA)); + return Integer.parseInt(userInfo.get(0)); + } + + /** + * 获取用户角色. + * + * @param token + * @return + */ + public static String getUserRole(String token) { + return (String) getTokenBody(token).get(ROLE_CLAIMS); + } + + /** + * 是否已过期. + * + * @param token + * @return + */ + public static boolean isExpiration(String token) { + try { + return getTokenBody(token).getExpiration().before(new Date()); + } catch (ExpiredJwtException e) { + return true; + } + } + + private static Claims getTokenBody(String token) { + return Jwts.parser() + .setSigningKey(SECRET) + .parseClaimsJws(token) + .getBody(); + } + +} diff --git a/auth/src/main/java/com/github/dynamic/threadpool/auth/toolkit/ReturnT.java b/auth/src/main/java/com/github/dynamic/threadpool/auth/toolkit/ReturnT.java new file mode 100644 index 00000000..1174eeca --- /dev/null +++ b/auth/src/main/java/com/github/dynamic/threadpool/auth/toolkit/ReturnT.java @@ -0,0 +1,40 @@ +package com.github.dynamic.threadpool.auth.toolkit; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * ReturnT. + * + * @author chen.ma + * @date 2021/11/10 00:00 + */ +@Data +@NoArgsConstructor +public class ReturnT implements Serializable { + + public static final long serialVersionUID = 42L; + + public static final int SUCCESS_CODE = 200; + public static final int FAIL_CODE = 500; + + public static final ReturnT SUCCESS = new ReturnT<>(null); + public static final ReturnT FAIL = new ReturnT<>(FAIL_CODE, null); + + private int code; + private String msg; + private T content; + + public ReturnT(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public ReturnT(T content) { + this.code = SUCCESS_CODE; + this.content = content; + } + +} diff --git a/pom.xml b/pom.xml index bd6736cc..12318860 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,7 @@ 6.1.5.Final 2.12.1 1.5.11 + 0.9.0 1.0.1 @@ -202,7 +203,7 @@ ${project.artifactId} - +