zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/common/config/ConfigProperties.java
New file @@ -0,0 +1,33 @@ package com.zy.asrs.wcs.common.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; /** * 系统配置属性 * * @author vincent * @since 2021-08-30 17:58:16 */ @Data @Configuration @ConfigurationProperties(prefix = "config") public class ConfigProperties { /** * token过期时间, 单位秒 */ private Long tokenExpireTime = 60 * 60 * 24L; /** * token快要过期自动刷新时间, 单位分钟 */ private int tokenRefreshTime = 30; /** * 生成token的密钥Key的base64字符 */ private String tokenKey; } zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/common/security/JwtAuthenticationFilter.java
@@ -1,112 +1,111 @@ //package com.zy.asrs.wcs.common.security; // //import com.core.common.Cools; //import com.zy.acs.manager.common.config.ConfigProperties; //import com.zy.acs.manager.common.constant.Constants; //import com.zy.acs.manager.common.utils.CommonUtil; //import com.zy.acs.manager.common.utils.JwtUtil; //import com.zy.acs.manager.system.entity.LoginRecord; //import com.zy.acs.manager.system.entity.Menu; //import com.zy.acs.manager.system.entity.User; //import com.zy.acs.manager.system.service.LoginRecordService; //import com.zy.acs.manager.system.service.UserService; //import io.jsonwebtoken.Claims; //import io.jsonwebtoken.ExpiredJwtException; //import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; //import org.springframework.security.core.context.SecurityContextHolder; //import org.springframework.security.core.userdetails.UsernameNotFoundException; //import org.springframework.security.web.util.matcher.AntPathRequestMatcher; //import org.springframework.stereotype.Component; //import org.springframework.web.filter.OncePerRequestFilter; // //import javax.annotation.Resource; //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.Date; //import java.util.List; //import java.util.stream.Collectors; // ///** // * 处理携带token的请求过滤器 // * // */ //@Component //public class JwtAuthenticationFilter extends OncePerRequestFilter { // // public static final ArrayList<String> WHITE_KEY = new ArrayList<String>(){ // private static final long serialVersionUID = 1L; // { // add("xltys1995"); // } // }; // // @Resource // private ConfigProperties configProperties; // @Resource // private UserService userService; // @Resource // private LoginRecordService loginRecordService; // // @Override // protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { // String access_token = JwtUtil.getAccessToken(request); // for (String filterPath : SecurityConfig.FILTER_PATH) { // AntPathRequestMatcher antPathMatcher = new AntPathRequestMatcher(filterPath); // if (antPathMatcher.matches(request)) { // access_token = ""; // } // } // if (!Cools.isEmpty(access_token)) { // try { // User user; // if (WHITE_KEY.contains(access_token)) { // user = userService.getByUsername("openapi", 2); // if (user == null) { // throw new UsernameNotFoundException("Username not found"); // } // List<Menu> authorities = user.getAuthorities().stream() // .filter(m -> !Cools.isEmpty(m.getAuthority())).collect(Collectors.toList()); // UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( // user, null, authorities); // SecurityContextHolder.getContext().setAuthentication(authentication); // } else { // // 解析token // Claims claims = JwtUtil.parseToken(access_token, configProperties.getTokenKey()); // JwtSubject jwtSubject = JwtUtil.getJwtSubject(claims); // user = userService.getByUsername(jwtSubject.getUsername(), jwtSubject.getTenantId()); // if (user == null) { // throw new UsernameNotFoundException("Username not found"); // } // List<Menu> authorities = user.getAuthorities().stream() // .filter(m -> !Cools.isEmpty(m.getAuthority())).collect(Collectors.toList()); // UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( // user, null, authorities); // SecurityContextHolder.getContext().setAuthentication(authentication); // // token将要过期签发新token, 防止突然退出登录 // long expiration = (claims.getExpiration().getTime() - new Date().getTime()) / 1000 / 60; // if (expiration < configProperties.getTokenRefreshTime()) { // String token = JwtUtil.buildToken(jwtSubject, configProperties.getTokenExpireTime(), // configProperties.getTokenKey()); // response.addHeader(Constants.TOKEN_HEADER_NAME, token); // loginRecordService.saveAsync(user.getUsername(), LoginRecord.TYPE_REFRESH, null, // user.getTenantId(), request); // } // } // } catch (ExpiredJwtException e) { // CommonUtil.responseError(response, Constants.TOKEN_EXPIRED_CODE, Constants.TOKEN_EXPIRED_MSG, // e.getMessage()); // return; // } catch (Exception e) { // CommonUtil.responseError(response, Constants.BAD_CREDENTIALS_CODE, Constants.BAD_CREDENTIALS_MSG, // e.toString()); // return; // } // } // chain.doFilter(request, response); // } // //} package com.zy.asrs.wcs.common.security; import com.zy.asrs.framework.common.Cools; import com.zy.asrs.wcs.common.config.ConfigProperties; import com.zy.asrs.wcs.common.constant.Constants; import com.zy.asrs.wcs.sys.entity.Menu; import com.zy.asrs.wcs.sys.entity.User; import com.zy.asrs.wcs.sys.service.UserLoginService; import com.zy.asrs.wcs.sys.service.UserService; import com.zy.asrs.wcs.utils.HttpUtils; import com.zy.asrs.wcs.utils.JwtUtil; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import javax.annotation.Resource; 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.Date; import java.util.List; import java.util.stream.Collectors; /** * 处理携带token的请求过滤器 * */ @Component public class JwtAuthenticationFilter extends OncePerRequestFilter { public static final ArrayList<String> WHITE_KEY = new ArrayList<String>(){ private static final long serialVersionUID = 1L; { add("xltys1995"); } }; @Resource private ConfigProperties configProperties; @Resource private UserService userService; @Resource private UserLoginService userLoginService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String access_token = JwtUtil.getAccessToken(request); for (String filterPath : SecurityConfig.FILTER_PATH) { AntPathRequestMatcher antPathMatcher = new AntPathRequestMatcher(filterPath); if (antPathMatcher.matches(request)) { access_token = ""; } } if (!Cools.isEmpty(access_token)) { try { User user; if (WHITE_KEY.contains(access_token)) { user = userService.getByUsername("openapi", 2); if (user == null) { throw new UsernameNotFoundException("Username not found"); } List<Menu> authorities = user.getAuthorities().stream() .filter(m -> !Cools.isEmpty(m.getAuthority())).collect(Collectors.toList()); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( user, null, authorities); SecurityContextHolder.getContext().setAuthentication(authentication); } else { // 解析token Claims claims = JwtUtil.parseToken(access_token, configProperties.getTokenKey()); JwtSubject jwtSubject = JwtUtil.getJwtSubject(claims); user = userService.getByUsername(jwtSubject.getUsername(), jwtSubject.getTenantId()); if (user == null) { throw new UsernameNotFoundException("Username not found"); } List<Menu> authorities = user.getAuthorities().stream() .filter(m -> !Cools.isEmpty(m.getAuthority())).collect(Collectors.toList()); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( user, null, authorities); SecurityContextHolder.getContext().setAuthentication(authentication); // token将要过期签发新token, 防止突然退出登录 long expiration = (claims.getExpiration().getTime() - new Date().getTime()) / 1000 / 60; if (expiration < configProperties.getTokenRefreshTime()) { String token = JwtUtil.buildToken(jwtSubject, configProperties.getTokenExpireTime(), configProperties.getTokenKey()); response.addHeader(Constants.TOKEN_HEADER_NAME, token); userLoginService.saveAsync(user.getUsername(), LoginRecord.TYPE_REFRESH, null, user.getTenantId(), request); } } } catch (ExpiredJwtException e) { HttpUtils.responseError(response, Constants.TOKEN_EXPIRED_CODE, Constants.TOKEN_EXPIRED_MSG, e.getMessage()); return; } catch (Exception e) { HttpUtils.responseError(response, Constants.BAD_CREDENTIALS_CODE, Constants.BAD_CREDENTIALS_MSG, e.toString()); return; } } chain.doFilter(request, response); } } zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/common/security/SecurityConfig.java
@@ -1,118 +1,118 @@ //package com.zy.asrs.wcs.common.security; // //import com.zy.acs.manager.common.constant.Constants; //import com.zy.acs.manager.common.utils.CommonUtil; //import org.springframework.context.annotation.Bean; //import org.springframework.context.annotation.Configuration; //import org.springframework.http.HttpMethod; //import org.springframework.security.access.AccessDeniedException; //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.AuthenticationException; //import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; //import org.springframework.security.web.AuthenticationEntryPoint; //import org.springframework.security.web.access.AccessDeniedHandler; //import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; //import org.springframework.stereotype.Component; // //import javax.annotation.Resource; //import javax.servlet.ServletException; //import javax.servlet.http.HttpServletRequest; //import javax.servlet.http.HttpServletResponse; //import java.io.IOException; // ///** // * Spring Security配置 // * // */ //@Configuration //@EnableWebSecurity //@EnableGlobalMethodSecurity(prePostEnabled = true) //public class SecurityConfig extends WebSecurityConfigurerAdapter { // // public static final String[] FILTER_PATH = new String[]{ // "/test/**", // "/rpc/**", // "/api/login", // "/druid/**", // "/swagger-ui.html", // "/swagger-resources/**", // "/webjars/**", // "/v2/api-docs", // "/v3/api-docs", // "/swagger-ui/**" // }; // // @Resource // private JwtAccessDeniedHandler jwtAccessDeniedHandler; // @Resource // private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; // @Resource // private JwtAuthenticationFilter jwtAuthenticationFilter; // // @Override // protected void configure(HttpSecurity http) throws Exception { // http.authorizeRequests() // .antMatchers(HttpMethod.OPTIONS, "/**") // .permitAll() // .antMatchers(HttpMethod.GET, "/api/file/**", "/api/captcha", "/") // .permitAll() // .antMatchers(FILTER_PATH) // .permitAll() // .anyRequest() // .authenticated() // .and() // .sessionManagement() // .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // .and() // .csrf() // .disable() // .cors() // .and() // .logout() // .disable() // .headers() // .frameOptions() // .disable() // .and() // .exceptionHandling() // .accessDeniedHandler(jwtAccessDeniedHandler) // .authenticationEntryPoint(jwtAuthenticationEntryPoint) // .and() // .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); // } // // @Bean // public BCryptPasswordEncoder bCryptPasswordEncoder() { // return new BCryptPasswordEncoder(); // } // // // 没有访问权限异常处理 // @Component // static class JwtAccessDeniedHandler implements AccessDeniedHandler { // // @Override // public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) // throws IOException, ServletException { // CommonUtil.responseError(response, Constants.UNAUTHORIZED_CODE, Constants.UNAUTHORIZED_MSG, e.getMessage()); // } // // } // // // 没有登录异常处理 // @Component // static class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { // // @Override // public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) // throws IOException, ServletException { // CommonUtil.responseError(response, Constants.UNAUTHENTICATED_CODE, Constants.UNAUTHENTICATED_MSG, // e.getMessage()); // } // // } // //} package com.zy.asrs.wcs.common.security; import com.zy.asrs.wcs.common.constant.Constants; import com.zy.asrs.wcs.utils.HttpUtils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.access.AccessDeniedException; 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.AuthenticationException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.stereotype.Component; import javax.annotation.Resource; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * Spring Security配置 * */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { public static final String[] FILTER_PATH = new String[]{ "/test/**", "/rpc/**", "/api/login", "/druid/**", "/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/v2/api-docs", "/v3/api-docs", "/swagger-ui/**" }; @Resource private JwtAccessDeniedHandler jwtAccessDeniedHandler; @Resource private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Resource private JwtAuthenticationFilter jwtAuthenticationFilter; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(HttpMethod.OPTIONS, "/**") .permitAll() .antMatchers(HttpMethod.GET, "/api/file/**", "/api/captcha", "/") .permitAll() .antMatchers(FILTER_PATH) .permitAll() .anyRequest() .authenticated() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .csrf() .disable() .cors() .and() .logout() .disable() .headers() .frameOptions() .disable() .and() .exceptionHandling() .accessDeniedHandler(jwtAccessDeniedHandler) .authenticationEntryPoint(jwtAuthenticationEntryPoint) .and() .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); } @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } // 没有访问权限异常处理 @Component static class JwtAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException { HttpUtils.responseError(response, Constants.UNAUTHORIZED_CODE, Constants.UNAUTHORIZED_MSG, e.getMessage()); } } // 没有登录异常处理 @Component static class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { HttpUtils.responseError(response, Constants.UNAUTHENTICATED_CODE, Constants.UNAUTHENTICATED_MSG, e.getMessage()); } } } zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/sys/entity/Menu.java
@@ -10,6 +10,7 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.security.core.GrantedAuthority; import java.io.Serializable; import java.text.SimpleDateFormat; @@ -17,10 +18,13 @@ @Data @TableName("sys_menu") public class Menu implements Serializable { public class Menu implements Serializable, GrantedAuthority { private static final long serialVersionUID = 1L; public static final int TYPE_MENU = 0; // 菜单类型 public static final int TYPE_BTN = 1; // 按钮类型 /** * ID */ zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/sys/entity/User.java
@@ -1,6 +1,7 @@ package com.zy.asrs.wcs.sys.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.zy.asrs.framework.common.Cools; @@ -11,14 +12,16 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.security.core.userdetails.UserDetails; import java.io.Serializable; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; @Data @TableName("sys_user") public class User implements Serializable { public class User implements Serializable, UserDetails { private static final long serialVersionUID = 1L; @@ -162,6 +165,9 @@ */ @ApiModelProperty(value= "备注") private String memo; @TableField(exist = false) private List<Menu> authorities; public User() {} @@ -315,5 +321,24 @@ return null; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return this.status != null && this.status == 1; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/sys/mapper/UserMapper.java
@@ -1,12 +1,17 @@ package com.zy.asrs.wcs.sys.mapper; import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import com.zy.asrs.wcs.sys.entity.User; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Mapper @Repository public interface UserMapper extends BaseMapper<User> { @InterceptorIgnore(tenantLine = "true") User selectByUsername(@Param("username") String username, @Param("hostId") Integer hostId); } zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/sys/service/UserService.java
@@ -5,4 +5,6 @@ public interface UserService extends IService<User> { User getByUsername(String username, Integer hostId); } zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/sys/service/impl/UserServiceImpl.java
@@ -1,12 +1,36 @@ package com.zy.asrs.wcs.sys.service.impl; import com.zy.asrs.wcs.sys.mapper.UserMapper; import com.zy.asrs.wcs.sys.entity.User; import com.zy.asrs.wcs.sys.service.UserService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.zy.asrs.framework.common.Cools; import com.zy.asrs.wcs.sys.entity.User; import com.zy.asrs.wcs.sys.mapper.UserMapper; import com.zy.asrs.wcs.sys.service.RoleMenuService; import com.zy.asrs.wcs.sys.service.UserRoleService; import com.zy.asrs.wcs.sys.service.UserService; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service("userService") public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { @Resource private UserRoleService userRoleService; @Resource private RoleMenuService roleMenuService; @Override public User getByUsername(String username, Integer hostId) { if (Cools.isEmpty(username)) { return null; } User user = baseMapper.selectByUsername(username, tenantId); if (user != null) { user.setRoles(userRoleService.listByUserId(user.getUserId())); user.setAuthorities(roleMenuService.listMenuByUserId(user.getUserId(), null)); } return user; } } zy-asrs-wcs/src/main/java/com/zy/asrs/wcs/utils/HttpUtils.java
New file @@ -0,0 +1,25 @@ package com.zy.asrs.wcs.utils; import com.alibaba.fastjson.JSON; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * Created by vincent on 1/29/2024 */ public class HttpUtils { public static void responseError(HttpServletResponse response, Integer code, String message, String error) { response.setContentType("application/json;charset=UTF-8"); try { PrintWriter out = response.getWriter(); out.write(JSON.toJSONString(new ApiResult<>(code, message, null, error))); out.flush(); } catch (IOException e) { e.printStackTrace(); } } } zy-asrs-wcs/src/main/resources/application.yml
@@ -45,3 +45,6 @@ super: pwd: xltys1995 config: token-key: KUHSMcYQ4lePt3r6bckz0P13cBJyoonYqInThvQlUnbsFCIcCcZZAbWZ6UNFztYNYPhGdy6eyb8WdIz8FU2Cz396TyTJk3NI2rtXMHBOehRb4WWJ4MdYVVg2oWPyqRQ2 zy-asrs-wcs/src/main/resources/mapper/sys/UserMapper.xml
@@ -2,4 +2,27 @@ <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zy.asrs.wcs.sys.mapper.UserMapper"> <!-- 根据账号查询 --> <select id="selectByUsername" resultType="com.zy.asrs.wcs.sys.entity.User"> SELECT a.* , b.organization_name, c.dict_data_name sex_name FROM sys_user a LEFT JOIN sys_organization b ON a.organization_id = b.organization_id LEFT JOIN ( <include refid="selectSexDictSql"/> ) c ON a.sex = c.dict_data_code <where> AND a.deleted = 0 AND a.username = #{username} <if test="tenantId != null"> AND a.tenant_id = #{tenantId} </if> <if test="tenantId == null"> AND a.tenant_id = 2 </if> </where> </select> </mapper>