diff --git a/mall-admin/pom.xml b/mall-admin/pom.xml index ca175d2..d66d5ae 100644 --- a/mall-admin/pom.xml +++ b/mall-admin/pom.xml @@ -28,6 +28,10 @@ com.macro.mall mall-mbg + + com.macro.mall + mall-security + org.springframework.boot spring-boot-starter-security diff --git a/mall-admin/src/main/java/com/macro/mall/component/JwtAuthenticationTokenFilter.java b/mall-admin/src/main/java/com/macro/mall/component/JwtAuthenticationTokenFilter.java deleted file mode 100644 index fe13f52..0000000 --- a/mall-admin/src/main/java/com/macro/mall/component/JwtAuthenticationTokenFilter.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.macro.mall.component; - -import com.macro.mall.util.JwtTokenUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.stereotype.Component; -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; - -/** - * JWT登录授权过滤器 - * Created by macro on 2018/4/26. - */ -public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { - private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class); - @Autowired - private UserDetailsService userDetailsService; - @Autowired - private JwtTokenUtil jwtTokenUtil; - @Value("${jwt.tokenHeader}") - private String tokenHeader; - @Value("${jwt.tokenHead}") - private String tokenHead; - - @Override - protected void doFilterInternal(HttpServletRequest request, - HttpServletResponse response, - FilterChain chain) throws ServletException, IOException { - String authHeader = request.getHeader(this.tokenHeader); - if (authHeader != null && authHeader.startsWith(this.tokenHead)) { - String authToken = authHeader.substring(this.tokenHead.length());// The part after "Bearer " - String username = jwtTokenUtil.getUserNameFromToken(authToken); - LOGGER.info("checking username:{}", username); - if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { - UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); - if (jwtTokenUtil.validateToken(authToken, userDetails)) { - UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); - authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - LOGGER.info("authenticated user:{}", username); - SecurityContextHolder.getContext().setAuthentication(authentication); - } - } - } - chain.doFilter(request, response); - } -} diff --git a/mall-admin/src/main/java/com/macro/mall/component/RestAuthenticationEntryPoint.java b/mall-admin/src/main/java/com/macro/mall/component/RestAuthenticationEntryPoint.java deleted file mode 100644 index 6a107ed..0000000 --- a/mall-admin/src/main/java/com/macro/mall/component/RestAuthenticationEntryPoint.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.macro.mall.component; - -import cn.hutool.json.JSONUtil; -import com.macro.mall.common.api.CommonResult; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.stereotype.Component; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * 当未登录或者token失效访问接口时,自定义的返回结果 - * Created by macro on 2018/5/14. - */ -@Component -public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint { - @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { - response.setCharacterEncoding("UTF-8"); - response.setContentType("application/json"); - response.getWriter().println(JSONUtil.parse(CommonResult.unauthorized(authException.getMessage()))); - response.getWriter().flush(); - } -} diff --git a/mall-admin/src/main/java/com/macro/mall/component/RestfulAccessDeniedHandler.java b/mall-admin/src/main/java/com/macro/mall/component/RestfulAccessDeniedHandler.java deleted file mode 100644 index 8b22012..0000000 --- a/mall-admin/src/main/java/com/macro/mall/component/RestfulAccessDeniedHandler.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.macro.mall.component; - -import cn.hutool.json.JSONUtil; -import com.macro.mall.common.api.CommonResult; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.web.access.AccessDeniedHandler; -import org.springframework.stereotype.Component; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * 当访问接口没有权限时,自定义的返回结果 - * Created by macro on 2018/4/26. - */ -@Component -public class RestfulAccessDeniedHandler implements AccessDeniedHandler{ - @Override - public void handle(HttpServletRequest request, - HttpServletResponse response, - AccessDeniedException e) throws IOException, ServletException { - response.setCharacterEncoding("UTF-8"); - response.setContentType("application/json"); - response.getWriter().println(JSONUtil.parse(CommonResult.forbidden(e.getMessage()))); - response.getWriter().flush(); - } -} diff --git a/mall-admin/src/main/java/com/macro/mall/config/MallSecurityConfig.java b/mall-admin/src/main/java/com/macro/mall/config/MallSecurityConfig.java new file mode 100644 index 0000000..a82ea2e --- /dev/null +++ b/mall-admin/src/main/java/com/macro/mall/config/MallSecurityConfig.java @@ -0,0 +1,29 @@ +package com.macro.mall.config; + +import com.macro.mall.security.config.SecurityConfig; +import com.macro.mall.service.UmsAdminService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.core.userdetails.UserDetailsService; + +/** + * mall-security模块相关配置 + * Created by macro on 2019/11/9. + */ +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled=true) +public class MallSecurityConfig extends SecurityConfig { + + @Autowired + private UmsAdminService adminService; + + @Bean + public UserDetailsService userDetailsService() { + //获取登录用户信息 + return username -> adminService.loadUserByUsername(username); + } +} diff --git a/mall-admin/src/main/java/com/macro/mall/config/SecurityConfig.java b/mall-admin/src/main/java/com/macro/mall/config/SecurityConfig.java deleted file mode 100644 index 1fe13ac..0000000 --- a/mall-admin/src/main/java/com/macro/mall/config/SecurityConfig.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.macro.mall.config; - -import com.macro.mall.bo.AdminUserDetails; -import com.macro.mall.component.JwtAuthenticationTokenFilter; -import com.macro.mall.component.RestAuthenticationEntryPoint; -import com.macro.mall.component.RestfulAccessDeniedHandler; -import com.macro.mall.model.UmsAdmin; -import com.macro.mall.model.UmsPermission; -import com.macro.mall.service.UmsAdminService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpMethod; -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; -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.core.userdetails.UsernameNotFoundException; -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.cors.CorsConfiguration; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.CorsFilter; - -import java.util.List; - - -/** - * SpringSecurity的配置 - * Created by macro on 2018/4/26. - */ -@Configuration -@EnableWebSecurity -@EnableGlobalMethodSecurity(prePostEnabled=true) -public class SecurityConfig extends WebSecurityConfigurerAdapter { - @Autowired - private UmsAdminService adminService; - @Autowired - private RestfulAccessDeniedHandler restfulAccessDeniedHandler; - @Autowired - private RestAuthenticationEntryPoint restAuthenticationEntryPoint; - - @Override - protected void configure(HttpSecurity httpSecurity) throws Exception { - httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf - .disable() - .sessionManagement()// 基于token,所以不需要session - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and() - .authorizeRequests() - .antMatchers(HttpMethod.GET, // 允许对于网站静态资源的无授权访问 - "/", - "/*.html", - "/favicon.ico", - "/**/*.html", - "/**/*.css", - "/**/*.js", - "/swagger-resources/**", - "/v2/api-docs/**", - "/webjars/springfox-swagger-ui/**" - ) - .permitAll() - .antMatchers("/admin/login", "/admin/register")// 对登录注册要允许匿名访问 - .permitAll() - .antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求 - .permitAll() -// .antMatchers("/**")//测试时全部运行访问 -// .permitAll() - .anyRequest()// 除上面外的所有请求全部需要鉴权认证 - .authenticated(); - // 禁用缓存 - httpSecurity.headers().cacheControl(); - // 添加JWT filter - httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class); - //添加自定义未授权和未登录结果返回 - httpSecurity.exceptionHandling() - .accessDeniedHandler(restfulAccessDeniedHandler) - .authenticationEntryPoint(restAuthenticationEntryPoint); - } - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth.userDetailsService(userDetailsService()) - .passwordEncoder(passwordEncoder()); - } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - - @Bean - public UserDetailsService userDetailsService() { - //获取登录用户信息 - return username -> { - UmsAdmin admin = adminService.getAdminByUsername(username); - if (admin != null) { - List permissionList = adminService.getPermissionList(admin.getId()); - return new AdminUserDetails(admin,permissionList); - } - throw new UsernameNotFoundException("用户名或密码错误"); - }; - } - - @Bean - public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){ - return new JwtAuthenticationTokenFilter(); - } - - @Bean - @Override - public AuthenticationManager authenticationManagerBean() throws Exception { - return super.authenticationManagerBean(); - } - -} diff --git a/mall-admin/src/main/java/com/macro/mall/service/UmsAdminService.java b/mall-admin/src/main/java/com/macro/mall/service/UmsAdminService.java index 520b1a8..ebf0cd4 100644 --- a/mall-admin/src/main/java/com/macro/mall/service/UmsAdminService.java +++ b/mall-admin/src/main/java/com/macro/mall/service/UmsAdminService.java @@ -5,6 +5,7 @@ import com.macro.mall.dto.UpdateAdminPasswordParam; import com.macro.mall.model.UmsAdmin; import com.macro.mall.model.UmsPermission; import com.macro.mall.model.UmsRole; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -84,4 +85,9 @@ public interface UmsAdminService { * 修改密码 */ int updatePassword(UpdateAdminPasswordParam updatePasswordParam); + + /** + * 获取用户信息 + */ + UserDetails loadUserByUsername(String username); } diff --git a/mall-admin/src/main/java/com/macro/mall/service/impl/UmsAdminServiceImpl.java b/mall-admin/src/main/java/com/macro/mall/service/impl/UmsAdminServiceImpl.java index 65cf5f5..eeb0613 100644 --- a/mall-admin/src/main/java/com/macro/mall/service/impl/UmsAdminServiceImpl.java +++ b/mall-admin/src/main/java/com/macro/mall/service/impl/UmsAdminServiceImpl.java @@ -3,6 +3,7 @@ package com.macro.mall.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import com.github.pagehelper.PageHelper; +import com.macro.mall.bo.AdminUserDetails; import com.macro.mall.dao.UmsAdminPermissionRelationDao; import com.macro.mall.dao.UmsAdminRoleRelationDao; import com.macro.mall.dto.UmsAdminParam; @@ -12,20 +13,18 @@ import com.macro.mall.mapper.UmsAdminMapper; import com.macro.mall.mapper.UmsAdminPermissionRelationMapper; import com.macro.mall.mapper.UmsAdminRoleRelationMapper; import com.macro.mall.model.*; +import com.macro.mall.security.util.JwtTokenUtil; import com.macro.mall.service.UmsAdminService; -import com.macro.mall.util.JwtTokenUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -47,15 +46,9 @@ import java.util.stream.Collectors; public class UmsAdminServiceImpl implements UmsAdminService { private static final Logger LOGGER = LoggerFactory.getLogger(UmsAdminServiceImpl.class); @Autowired - private AuthenticationManager authenticationManager; - @Autowired - private UserDetailsService userDetailsService; - @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private PasswordEncoder passwordEncoder; - @Value("${jwt.tokenHead}") - private String tokenHead; @Autowired private UmsAdminMapper adminMapper; @Autowired @@ -105,7 +98,7 @@ public class UmsAdminServiceImpl implements UmsAdminService { String token = null; //密码需要客户端加密后传递 try { - UserDetails userDetails = userDetailsService.loadUserByUsername(username); + UserDetails userDetails = loadUserByUsername(username); if(!passwordEncoder.matches(password,userDetails.getPassword())){ throw new BadCredentialsException("密码不正确"); } @@ -148,11 +141,7 @@ public class UmsAdminServiceImpl implements UmsAdminService { @Override public String refreshToken(String oldToken) { - String token = oldToken.substring(tokenHead.length()); - if (jwtTokenUtil.canRefresh(token)) { - return jwtTokenUtil.refreshToken(token); - } - return null; + return jwtTokenUtil.refreshHeadToken(oldToken); } @Override @@ -274,4 +263,15 @@ public class UmsAdminServiceImpl implements UmsAdminService { adminMapper.updateByPrimaryKey(umsAdmin); return 1; } + + @Override + public UserDetails loadUserByUsername(String username){ + //获取用户信息 + UmsAdmin admin = getAdminByUsername(username); + if (admin != null) { + List permissionList = getPermissionList(admin.getId()); + return new AdminUserDetails(admin,permissionList); + } + throw new UsernameNotFoundException("用户名或密码错误"); + } } diff --git a/mall-admin/src/main/java/com/macro/mall/util/JwtTokenUtil.java b/mall-admin/src/main/java/com/macro/mall/util/JwtTokenUtil.java deleted file mode 100644 index 15bcbf1..0000000 --- a/mall-admin/src/main/java/com/macro/mall/util/JwtTokenUtil.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.macro.mall.util; - -import io.jsonwebtoken.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.stereotype.Component; - -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -/** - * JwtToken生成的工具类 - * JWT token的格式:header.payload.signature - * header的格式(算法、token的类型): - * {"alg": "HS512","typ": "JWT"} - * payload的格式(用户名、创建时间、生成时间): - * {"sub":"wang","created":1489079981393,"exp":1489684781} - * signature的生成算法: - * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret) - * Created by macro on 2018/4/26. - */ -@Component -public class JwtTokenUtil { - private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class); - private static final String CLAIM_KEY_USERNAME = "sub"; - private static final String CLAIM_KEY_CREATED = "created"; - @Value("${jwt.secret}") - private String secret; - @Value("${jwt.expiration}") - private Long expiration; - - /** - * 根据负责生成JWT的token - */ - private String generateToken(Map claims) { - return Jwts.builder() - .setClaims(claims) - .setExpiration(generateExpirationDate()) - .signWith(SignatureAlgorithm.HS512, secret) - .compact(); - } - - /** - * 从token中获取JWT中的负载 - */ - private Claims getClaimsFromToken(String token) { - Claims claims = null; - try { - claims = Jwts.parser() - .setSigningKey(secret) - .parseClaimsJws(token) - .getBody(); - } catch (Exception e) { - LOGGER.info("JWT格式验证失败:{}",token); - } - return claims; - } - - /** - * 生成token的过期时间 - */ - private Date generateExpirationDate() { - return new Date(System.currentTimeMillis() + expiration * 1000); - } - - /** - * 从token中获取登录用户名 - */ - public String getUserNameFromToken(String token) { - String username; - try { - Claims claims = getClaimsFromToken(token); - username = claims.getSubject(); - } catch (Exception e) { - username = null; - } - return username; - } - - /** - * 验证token是否还有效 - * - * @param token 客户端传入的token - * @param userDetails 从数据库中查询出来的用户信息 - */ - public boolean validateToken(String token, UserDetails userDetails) { - String username = getUserNameFromToken(token); - return username.equals(userDetails.getUsername()) && !isTokenExpired(token); - } - - /** - * 判断token是否已经失效 - */ - private boolean isTokenExpired(String token) { - Date expiredDate = getExpiredDateFromToken(token); - return expiredDate.before(new Date()); - } - - /** - * 从token中获取过期时间 - */ - private Date getExpiredDateFromToken(String token) { - Claims claims = getClaimsFromToken(token); - return claims.getExpiration(); - } - - /** - * 根据用户信息生成token - */ - public String generateToken(UserDetails userDetails) { - Map claims = new HashMap<>(); - claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername()); - claims.put(CLAIM_KEY_CREATED, new Date()); - return generateToken(claims); - } - - /** - * 判断token是否可以被刷新 - */ - public boolean canRefresh(String token) { - return !isTokenExpired(token); - } - - /** - * 刷新token - */ - public String refreshToken(String token) { - Claims claims = getClaimsFromToken(token); - claims.put(CLAIM_KEY_CREATED, new Date()); - return generateToken(claims); - } -} diff --git a/mall-admin/src/main/resources/application.yml b/mall-admin/src/main/resources/application.yml index 99a7022..c0b10c0 100644 --- a/mall-admin/src/main/resources/application.yml +++ b/mall-admin/src/main/resources/application.yml @@ -9,9 +9,24 @@ mybatis: jwt: tokenHeader: Authorization #JWT存储的请求头 - secret: mySecret #JWT加解密使用的密钥 + secret: mall-admin-secret #JWT加解密使用的密钥 expiration: 604800 #JWT的超期限时间(60*60*24) tokenHead: Bearer #JWT负载中拿到开头 +ignored: #安全路径白名单 + urls: + - /swagger-ui.html + - /swagger-resources/** + - /swagger/** + - /**/v2/api-docs + - /**/*.js + - /**/*.css + - /**/*.png + - /**/*.ico + - /webjars/springfox-swagger-ui/** + - /actuator/** + - /druid/** + - /admin/login + - /admin/register aliyun: oss: diff --git a/pom.xml b/pom.xml index dc1af27..c8b7ded 100644 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,7 @@ 4.8 1.0-SNAPSHOT 1.0-SNAPSHOT + 1.0-SNAPSHOT @@ -88,6 +89,12 @@ mall-mbg ${mall-mbg.version} + + + com.macro.mall + mall-security + ${mall-security.version} + com.github.pagehelper