数据库结构准备
需要基础表:_user、_role、_user_role、_permission、_role_permission,其中,用户表结构可以不用这么多,但是角色、权限表那些就需要保持一样,才能够跟配置类对应上。
/*
Navicat Premium Data Transfer
Source Server : localhost
Source Server Type : MySQL
Source Server Version : 50650
Source Host : localhost:3306
Source Schema : hlsd100
Target Server Type : MySQL
Target Server Version : 50650
File Encoding : 65001
Date: 19/11/2021 15:07:47
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for _permission
-- ----------------------------
DROP TABLE IF EXISTS `_permission`;
CREATE TABLE `_permission` (
`permission_id` int(11) NOT NULL AUTO_INCREMENT,
`permission_title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`pid` int(11) NOT NULL DEFAULT -1 COMMENT '默认-1,-1代表匿名url,其他代表需要权限。',
PRIMARY KEY (`permission_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact;
-- ----------------------------
-- Records of _permission
-- ----------------------------
INSERT INTO `_permission` VALUES (1, '获取用户数组', '/user/**', -1);
INSERT INTO `_permission` VALUES (2, '匿名可访问url', '/swagger-ui.html', -1);
INSERT INTO `_permission` VALUES (3, '匿名可访问url', '/swagger-resources/*', -1);
INSERT INTO `_permission` VALUES (4, '匿名可访问url', '/webjars/*', -1);
INSERT INTO `_permission` VALUES (5, '匿名可访问url', '/*/api-docs', -1);
INSERT INTO `_permission` VALUES (6, '匿名可访问url', '/static/**', -1);
INSERT INTO `_permission` VALUES (7, '匿名可访问url', '/api/auth/**', -1);
INSERT INTO `_permission` VALUES (8, '匿名可访问url', '/error/**', -1);
INSERT INTO `_permission` VALUES (9, '匿名可访问url', '/webSocket/**', -1);
INSERT INTO `_permission` VALUES (10, '匿名可访问url', '/favicon.ico', -1);
INSERT INTO `_permission` VALUES (11, '匿名可访问url', '/auth/**', -1);
-- ----------------------------
-- Table structure for _role
-- ----------------------------
DROP TABLE IF EXISTS `_role`;
CREATE TABLE `_role` (
`role_id` int(11) NOT NULL AUTO_INCREMENT,
`role_title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
PRIMARY KEY (`role_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact;
-- ----------------------------
-- Records of _role
-- ----------------------------
INSERT INTO `_role` VALUES (1, 'admin');
INSERT INTO `_role` VALUES (2, 'root');
INSERT INTO `_role` VALUES (3, 'user');
-- ----------------------------
-- Table structure for _role_permission
-- ----------------------------
DROP TABLE IF EXISTS `_role_permission`;
CREATE TABLE `_role_permission` (
`role_permission_id` int(11) NOT NULL AUTO_INCREMENT,
`role_id` int(11) NULL DEFAULT NULL,
`permission_id` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`role_permission_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact;
-- ----------------------------
-- Records of _role_permission
-- ----------------------------
INSERT INTO `_role_permission` VALUES (1, 1, 1);
INSERT INTO `_role_permission` VALUES (2, 2, 1);
-- ----------------------------
-- Table structure for _user
-- ----------------------------
DROP TABLE IF EXISTS `_user`;
CREATE TABLE `_user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`user_gender` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`user_tel` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`user_email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`user_logo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`u_acco` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '用户名',
`u_pass` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '密码',
`user_upper_a` int(11) NOT NULL DEFAULT 0 COMMENT '上级a,这里指的是代理商。-1代表没绑定,否则表示是代理商的用户id',
`user_balance` int(11) NOT NULL DEFAULT 0 COMMENT '余额,如果提现了需要减去,这个字段普通用户跟代理商都会有,分销的时候是加到这个字段,',
`user_total_balance` int(11) NOT NULL DEFAULT 0 COMMENT ' 总余额,不管有没有提现,都不会减少。-代理商收入跟分销收入都会加到这里',
`user_upper_b` int(11) NOT NULL DEFAULT 0 COMMENT '上级b,这里指的是代理商。-1代表没绑定,否则表示是代理商的用户id',
`createtime` bigint(13) NULL DEFAULT NULL,
`user_sale_amount` int(11) NOT NULL DEFAULT 0 COMMENT ' 销售额度,代理商才会有,普通用户可以不用考虑该字段,代理商收入会添加到这个字段',
`user_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT ' 二维码',
`user_visit` int(11) NOT NULL DEFAULT 0,
`user_version_total` int(11) NOT NULL DEFAULT 0 COMMENT ' ',
`is_agent` int(11) NOT NULL DEFAULT 0,
`last_login_time` bigint(13) NULL DEFAULT NULL,
`user_wx_no` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`invite_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 517 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact;
-- ----------------------------
-- Records of _user
-- ----------------------------
INSERT INTO `_user` VALUES (1, 'admin', NULL, NULL, NULL, NULL, 'admin', 'admin', 0, 0, 0, 0, NULL, 0, NULL, 19, 10, 1, NULL, NULL, NULL);
INSERT INTO `_user` VALUES (516, 'ean', '男', '4008823823', '984960659@qq.com', NULL, 'ean', '123456', 0, 0, 0, 0, NULL, 0, NULL, 0, 0, 0, NULL, NULL, NULL);
-- ----------------------------
-- Table structure for _user_role
-- ----------------------------
DROP TABLE IF EXISTS `_user_role`;
CREATE TABLE `_user_role` (
`user_role_id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NULL DEFAULT NULL,
`role_id` int(11) NULL DEFAULT NULL,
`user_title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`user_avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`role_title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`status` int(1) NOT NULL DEFAULT 1,
PRIMARY KEY (`user_role_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact;
SET FOREIGN_KEY_CHECKS = 1;
jwt相关的工具类
JwtTokenUtils
@Slf4j
@Component
public class JwtTokenUtils implements InitializingBean {
private final JwtSecurityProperties jwtSecurityProperties;
private static final String AUTHORITIES_KEY = "auth";
private Key key;
@Value(("${jwt.base64-secret}"))
private String base64Secret;
public JwtTokenUtils(JwtSecurityProperties jwtSecurityProperties) {
this.jwtSecurityProperties = jwtSecurityProperties;
}
@Override
public void afterPropertiesSet() {
byte[] keyBytes = Decoders.BASE64.decode(base64Secret);
this.key = Keys.hmacShaKeyFor(keyBytes);
}
/*登陆的时候调用这个函数生成token,并记录用户信息以及角色,这个claims参数必须要有roles*/
public String createToken(Map<String, Object> claims) {
String roles = String.valueOf(claims.get("roles"));
String ip = String.valueOf(claims.get("ip"));
if (null == roles || "".equals(roles)) {
roles = "user";
}
Date date = new Date();
return Jwts.builder()
.claim(AUTHORITIES_KEY, claims)
.claim("ROLE", roles)
.claim("IP", ip)
.setId(UUID.randomUUID().toString())
.setIssuedAt(date)
.setExpiration(new Date(date.getTime() + jwtSecurityProperties.getTokenValidityInSeconds()))
.compressWith(CompressionCodecs.DEFLATE)
.signWith(key, SignatureAlgorithm.HS512)
.compact();
}
public Date getExpirationDateFromToken(String token) {
Date expiration;
try {
final Claims claims = getClaimsFromToken(token);
expiration = claims.getExpiration();
} catch (Exception e) {
expiration = null;
}
return expiration;
}
/**
* @Author Ean
* @Description 解析token,返回token的角色数组
* @Date 2021/11/18
*/
public Authentication getAuthentication(String token, HttpServletRequest httpServletRequest) {
if ("Yz78r4DSn3yZrSgT".equals(token)) {//处理携带特殊token的情况
return new UsernamePasswordAuthenticationToken("", token, null);
}
Claims claims = Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(token)
.getBody();
String ip = String.valueOf(claims.get("IP"));
try {
String remoteIp = IpUtils.getIpAddr(httpServletRequest);
if (!remoteIp.equals(ip)) {
log.error("本次请求IP跟token所携带的IP不一致");
throw new Exception("本次请求IP跟token所携带的IP不一致");
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
// log.info("ROLE->" + claims.get("ROLE"));
Collection<? extends GrantedAuthority> authorities =
Arrays.stream(claims.get("ROLE").toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
HashMap map = (HashMap) claims.get("auth");
User principal = new User(map.get("username").toString(), map.get("password").toString(), authorities);
return new UsernamePasswordAuthenticationToken(principal, token, authorities);
}
/*校验token格式是否正确*/
public boolean validateToken(String authToken, HttpServletRequest httpServletRequest) {
try {
Claims claims = Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(authToken)
.getBody();
String ip = String.valueOf(claims.get("IP"));
try {
String remoteIp = IpUtils.getIpAddr(httpServletRequest);
if (!remoteIp.equals(ip)) {
log.error("本次请求IP跟token所携带的IP不一致");
throw new Exception("本次请求IP跟token所携带的IP不一致");
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
} catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
log.error("Invalid JWT signature.");
// e.printStackTrace();
} catch (ExpiredJwtException e) {
log.error("Expired JWT token.");
// e.printStackTrace();
} catch (UnsupportedJwtException e) {
log.error("Unsupported JWT token.");
// e.printStackTrace();
} catch (IllegalArgumentException e) {
log.error("JWT token compact of handler are invalid.");
// e.printStackTrace();
}
return false;
}
private Claims getClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
claims = null;
}
return claims;
}
IpUtils ip获取工具类
public class IpUtils {
public static String getIpAddr(HttpServletRequest request) throws Exception {
if (request == null) {
throw (new Exception("getIpAddr method HttpServletRequest Object is null"));
}
String ipString = request.getHeader("x-forwarded-for");
if (StringUtils.isBlank(ipString) || "unknown".equalsIgnoreCase(ipString)) {
ipString = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isBlank(ipString) || "unknown".equalsIgnoreCase(ipString)) {
ipString = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isBlank(ipString) || "unknown".equalsIgnoreCase(ipString)) {
ipString = request.getRemoteAddr();
}
// 多个路由时,取第一个非unknown的ip
final String[] arr = ipString.split(",");
for (final String str : arr) {
if (!"unknown".equalsIgnoreCase(str)) {
ipString = str;
break;
}
}
return ipString;
}
}
yml配置文件
#jwt
jwt:
header: Authorization
# 令牌前缀
token-start-with: Bearer
# 使用Base64对该令牌进行编码
base64-secret: 44af5348f21c5ea9d9f80ccc5bddfbf3beebc794b9a4a6258573a841170475a03a03bbd44851882faa0eb2a929ecb0c2e1df12498dafb2764aac83d9d3f5d82d
# 令牌过期时间 此处单位/毫秒-现在是20分钟
token-validity-in-seconds: 1200000
spring security相关java类
JwtAccessDeniedHandler
/**
* @Author Ean
* @Description 没有授权则通过此类返回信息
* @Date 2021/11/18
*/
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage());
}
}
JwtAuthenticationEntryPoint
/**
* @Author Ean
* @Description 没有权限则通过此类返回信息
* @Date 2021/11/18
*/
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException == null ? "Unauthorized" : authException.getMessage());
}
}
UrlAccessDecisionManager
/**
* @author Ean
* @version 0.1.0
* @Description: 该类的作用是判断用户是否有权限访问当前的url
* @return
* @date 2020/8/27 18:06
* @since 0.1.0
*/
@Component
public class UrlAccessDecisionManager implements AccessDecisionManager {
/**
* @param authentication: 当前登录用户的角色信息
* @param object: 请求url信息
* @param collection: `UrlFilterInvocationSecurityMetadataSource`中的getAttributes方法传来的,表示当前请求需要的角色(可能有多个)
* @return: void
*/
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> collection) throws AccessDeniedException, AuthenticationException {
if (null != authentication && authentication.getCredentials().equals("Yz78r4DSn3yZrSgT")) {//跳过有这个token的请求
return;
}
// 遍历角色
for (ConfigAttribute ca : collection) {
// ① 当前url请求需要的权限
String needRole = ca.getAttribute();
if (Constants.ROLE_LOGIN.equals(needRole)) {
if (authentication instanceof AnonymousAuthenticationToken) {
throw new BadCredentialsException("未登录!");
} else {
throw new AccessDeniedException("未授权该url!");
}
}
// ② 当前用户所具有的角色
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (GrantedAuthority authority : authorities) {
// 只要包含其中一个角色即可访问
if (authority.getAuthority().equals(needRole)) {
return;
}
}
}
throw new AccessDeniedException("无权访问该接口");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
UrlAccessDeniedHandler
@Component
public class UrlAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
response.setContentType("application/json");
response.getOutputStream().print(JSONObject.toJSONString(ResultUtil.error(403, "User does not have permission")));
}
}
UrlFilterInvocationSecurityMetadataSource
/**
* @author Ean
* @version 0.1.0
* @Description: 这个类也比较重要。重写的是security的接口权限判断方法。
* getAttributes函数会去数据库查询某个url所需要的权限
* @return
* @date 2020/8/27 18:07
* @since 0.1.0
*/
@Component
public class UrlFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
@Autowired
IPermissionService iPermissionService;
@Autowired
IRolePermissionService iRolePermissionService;
@Autowired
IRoleService iRoleService;
AntPathMatcher antPathMatcher = new AntPathMatcher();
/***
* 返回该url所需要的用户权限信息
*
* @param object: 储存请求url信息
* @return: null:标识不需要任何权限都可以访问
*/
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
// 获取当前请求url
String requestUrl = ((FilterInvocation) object).getRequestUrl();
/*放行匿名URL*/
if (includeUrl(requestUrl)) {
return null;
}
// 数据库中所有url
Permission[] list = iPermissionService.list(new QueryWrapper<Permission>().ne("pid", -1)).toArray(new Permission[0]);
for (Permission permission : list) {
if (null != permission && antPathMatcher.match(permission.getUrl(), requestUrl)) {
RolePermission[] rolePermissions = iRolePermissionService.list(new QueryWrapper<RolePermission>().eq("permission_id", permission.getPermissionId())).toArray(new RolePermission[0]);
String[] roles = new String[rolePermissions.length];
int i = 0;
if (rolePermissions.length != 0) {
Role role;
for (RolePermission rolePermission : rolePermissions) {
role = iRoleService.getById(rolePermission.getRoleId());
roles[i++] = role.getRoleTitle();
}
}
if (roles.length == 0) {
roles = new String[]{Constants.ROLE_LOGIN};
}
// 保存该url对应角色权限信息
return SecurityConfig.createList(roles);
}
}
// 如果数据中没有找到相应url资源则为非法访问,要求用户登录再进行操作
String[] roleArr = {"user"};//默认角色都是user
return SecurityConfig.createList(roleArr);
}
private boolean includeUrl(String requestUrl) {
AntPathMatcher antPathMatcher = new AntPathMatcher();
boolean flag;
for (Permission permission : WebSecurityConfig.getANNO_URL_LIST()) {
flag = antPathMatcher.match(permission.getUrl(), requestUrl);
if (flag) {
return true;
}
}
return false;
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> aClass) {
return FilterInvocation.class.isAssignableFrom(aClass);
}
}
过滤器配置
WebSecurityConfig
/**
* @Author Ean
* @Description 总过滤器
* @Date 2021/11/18
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
private final JwtTokenUtils jwtTokenUtils;
public static List<Permission> ANNO_URL_LIST = new ArrayList();
/*获取匿名可访问数组*/
public static List<Permission> getANNO_URL_LIST() {
if (ANNO_URL_LIST.size() <= 0) {
IPermissionService iPermissionService = (IPermissionService) SpringContextUtil.getBean(IPermissionService.class);
ANNO_URL_LIST = iPermissionService.list(new QueryWrapper<Permission>().eq("pid", -1));
}
return ANNO_URL_LIST;
}
/*更新匿名可访问数组*/
public static void flushANNO_URL_LIST() {
IPermissionService iPermissionService = (IPermissionService) SpringContextUtil.getBean(IPermissionService.class);
ANNO_URL_LIST = iPermissionService.list(new QueryWrapper<Permission>().eq("pid", -1));
}
/**
* 获取访问url所需要的角色信息
*/
private final UrlFilterInvocationSecurityMetadataSource urlFilterInvocationSecurityMetadataSource;
/**
* 认证权限处理 - 将上面所获得角色权限与当前登录用户的角色做对比,如果包含其中一个角色即可正常访问
*/
private final UrlAccessDecisionManager urlAccessDecisionManager;
/**
* 自定义访问无权限接口时403响应内容
*/
private final UrlAccessDeniedHandler urlAccessDeniedHandler;
public WebSecurityConfig(JwtAccessDeniedHandler jwtAccessDeniedHandler, JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint, JwtTokenUtils jwtTokenUtils, UrlFilterInvocationSecurityMetadataSource urlFilterInvocationSecurityMetadataSource, UrlAccessDecisionManager urlAccessDecisionManager, UrlAccessDeniedHandler urlAccessDeniedHandler) {
this.jwtAccessDeniedHandler = jwtAccessDeniedHandler;
this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;
this.jwtTokenUtils = jwtTokenUtils;
this.urlFilterInvocationSecurityMetadataSource = urlFilterInvocationSecurityMetadataSource;
this.urlAccessDecisionManager = urlAccessDecisionManager;
this.urlAccessDeniedHandler = urlAccessDeniedHandler;
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors().and()
// 禁用 CSRF
.csrf().disable()
// 授权异常
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.accessDeniedHandler(jwtAccessDeniedHandler)
// 防止iframe 造成跨域
.and()
.headers()
.frameOptions()
.disable()
// 不创建会话
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 所有请求都需要认证
.anyRequest().authenticated().withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setSecurityMetadataSource(urlFilterInvocationSecurityMetadataSource);
o.setAccessDecisionManager(urlAccessDecisionManager);
return o;
}
});
;
// 登录过后访问无权限的接口时自定义403响应内容
httpSecurity.exceptionHandling().accessDeniedHandler(urlAccessDeniedHandler);
;
// 登录过后访问无权限的接口时自定义403响应内容
httpSecurity.exceptionHandling().accessDeniedHandler(urlAccessDeniedHandler);
// 禁用缓存
httpSecurity.headers().cacheControl();
// 添加JWT filter
httpSecurity.apply(new TokenConfigurer(jwtTokenUtils));
}
public static class TokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private final JwtTokenUtils jwtTokenUtils;
public TokenConfigurer(JwtTokenUtils jwtTokenUtils) {
this.jwtTokenUtils = jwtTokenUtils;
}
@Override
public void configure(HttpSecurity http) {
JwtAuthenticationTokenFilter customFilter = new JwtAuthenticationTokenFilter(jwtTokenUtils);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
}
CorsConfigure
/**
* @Author Ean
* @Description 跨域配置
* @Date 2021/11/18
*/
@Configuration
public class CorsConfigure {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
MySecurityProperties mySecurityProperties = new MySecurityProperties();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedHeaders(mySecurityProperties.corsAllowedHeaders);
corsConfiguration.setAllowedMethods(mySecurityProperties.corsAllowedMethods);
corsConfiguration.setAllowedOrigins(mySecurityProperties.corsAllowedOrigins);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration(mySecurityProperties.corsPath, corsConfiguration);
return source;
}
private class MySecurityProperties {
/*允许跨域的资源路径*/
private String corsPath;
/*允许跨域的请求头*/
private List<String> corsAllowedHeaders;
/*允许跨域的方法*/
private List<String> corsAllowedMethods;
/*允许跨的站点*/
private List<String> corsAllowedOrigins;
private final List<String> CORS_ALLOWED_HEADERS = Arrays.asList("Authorization", "Content-type");
private final List<String> CORS_ALLOWED_METHODS = Arrays.asList("GET", "POST", "PUT", "DELETE");
public MySecurityProperties() {
this.corsPath = "/**";
this.corsAllowedHeaders = CORS_ALLOWED_HEADERS;
this.corsAllowedMethods = CORS_ALLOWED_METHODS;
this.corsAllowedOrigins = Arrays.asList("*");
}
}
}
JwtAuthenticationTokenFilter
/**
* @author Ean
* @version 0.1.0
* @Description: 重点过滤器,该过滤器拦截请求,并校验token。通过则提取角色信息交给security处理
* @return
* @date 2020/8/27 18:05
* @since 0.1.0
*/
@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
private JwtTokenUtils jwtTokenUtils;
public JwtAuthenticationTokenFilter(JwtTokenUtils jwtTokenUtils) {
this.jwtTokenUtils = jwtTokenUtils;
}
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
JwtSecurityProperties jwtSecurityProperties = (JwtSecurityProperties) SpringContextUtil.getBean(JwtSecurityProperties.class);
String requestRri = httpServletRequest.getRequestURI();
//获取request token
String token = null;
String bearerToken = httpServletRequest.getHeader(jwtSecurityProperties.getHeader());
if (null!=bearerToken&&bearerToken.equals("Yz78r4DSn3yZrSgT")) {//忽略带有此token的请求
Authentication authentication = jwtTokenUtils.getAuthentication(bearerToken, httpServletRequest);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(jwtSecurityProperties.getTokenStartWith())) {
token = bearerToken.substring(jwtSecurityProperties.getTokenStartWith().length());
}
/*访问接口的时候,判断token是否有效并从token里边提取用户信息跟角色记录到此次请求中*/
if (StringUtils.hasText(token) && jwtTokenUtils.validateToken(token,httpServletRequest)) {
Authentication authentication = jwtTokenUtils.getAuthentication(token,httpServletRequest);
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(httpServletRequest, httpServletResponse);
}
}
授权控制器
AuthController
@Slf4j
@RestController
@RequestMapping("/api/auth")
@Api(tags = "系统授权接口")
public class AuthController {
private final JwtTokenUtils jwtTokenUtils;
final
IPermissionService iPermissionService;
final
IUserService userService;
public AuthController(JwtTokenUtils jwtTokenUtils, IPermissionService iPermissionService, IUserService userService) {
this.jwtTokenUtils = jwtTokenUtils;
this.iPermissionService = iPermissionService;
this.userService = userService;
}
@ApiOperation("登录授权")
@PostMapping(value = "/login")
public Result login(String uAcco, String uPass, HttpServletRequest httpServletRequest) {
Map resMap = userService.checkAndGetToken(uAcco, uPass,httpServletRequest);
if (null != resMap) {
return ResultUtil.success(resMap);
}
return ResultUtil.error(-1, "用户名密码错误");
}
@ApiOperation("刷新匿名url")
@GetMapping("flushANNO_URL")
public String flushANNO_URL() {
IPermissionService iPermissionService = (IPermissionService) SpringContextUtil.getBean(IPermissionService.class);
List<Permission> ANNO_URL_LIST = iPermissionService.list(new QueryWrapper<Permission>().eq("pid", -1));
WebSecurityConfig.ANNO_URL_LIST = ANNO_URL_LIST;
return "flush ok";
}
}
checkAndGetToken
@Override
public Map checkAndGetToken(String uAcco, String uPass, HttpServletRequest servletRequest) {
String ip="";
try {
ip = IpUtils.getIpAddr(servletRequest);
} catch (Exception e) {
e.printStackTrace();
}
User user = this.getOne(new QueryWrapper<User>().eq("u_acco", uAcco).eq("u_pass", uPass));
if (null != user) {
Map param = new HashMap();
param.put("username", uAcco);
param.put("password", uPass);
param.put("ip", ip);
String[] roleTitle = roleMapper.getRoleTitle(user.getUserId());
if (roleTitle.length > 0) {
param.put("roles", StringUtils.join(roleTitle, ","));
}
Map res = new HashMap();
res.put("Authorization", jwtTokenUtils.createToken(param));
res.put("user", user);
return res;
}
return null;
}
pom文件
<!--Security框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- jwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.10.6</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.10.6</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.10.6</version>
</dependency>