项目已经集成了springsecurity并已上传
以下代码看看就好了gitee上有完整代码
添加的依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.1</version> <!-- 或更高版本 --></dependency><dependency><groupId>org.glassfish.jaxb</groupId><artifactId>jaxb-runtime</artifactId><version>2.3.1</version> <!-- 或更高版本 --></dependency><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.8.1</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.75</version></dependency>
utils工具类
EmptyUtil
/*** 判断对象是否为空的工具类*/
public abstract class EmptyUtil {/**** 对string字符串是否为空判断** @param str 被判定字符串* @return*/public static boolean isNullOrEmpty(String str) {if (str == null || "".equals(str.trim()) || "null".equalsIgnoreCase(str.trim()) || "undefined".equalsIgnoreCase(str.trim())) {return true;} else {return false;}}/**** 对于StringBuffer类型的非空判断** @param str 被判定StringBuffer* @return*/public static boolean isNullOrEmpty(StringBuffer str) {return (str == null || str.length() == 0);}/**** 对于string数组类型的非空判断** @param str 被判定字符串数组* @return*/public static boolean isNullOrEmpty(String[] str) {if (str == null || str.length == 0) {return true;} else {return false;}}/**** 对于Object类型的非空判断** @param obj 被判定对象* @return*/public static boolean isNullOrEmpty(Object obj) {if (obj == null || "".equals(obj)) {return true;} else {return false;}}/**** 对于Object数组类型的非空判断** @param obj 被判定对象数组* @return*/public static boolean isNullOrEmpty(Object[] obj) {if (obj == null || obj.length == 0) {return true;} else {return false;}}/**** 对于Collection类型的非空判断** @param collection 被判定Collection类型对象* @return*/public static boolean isNullOrEmpty(Collection collection) {if (collection == null || collection.isEmpty()) {return true;} else {return false;}}/*** @方法名:对于Map类型的非空判断* @功能说明:对于Map类型的非空判断* @return boolean true-为空,false-不为空* @throws*/@SuppressWarnings("rawtypes")public static boolean isNullOrEmpty( Map map) {if (map == null || map.isEmpty()) {return true;} else {return false;}}/**** @方法名:removeNullUnit* @功能说明: 删除集合中的空元素* @return*/public static <T> List<T> removeNullUnit(List<T> xllxList) {List<T> need = new ArrayList<T>();for (int i = 0; i < xllxList.size(); i++) {if (!isNullOrEmpty(xllxList.get(i))) {need.add(xllxList.get(i));}}return need;}}
JWTUtil
public class JwtUtil {/*** 生成jwt* 使用Hs256算法, 私匙使用固定秘钥** @param secretKey jwt秘钥* @param dateOffset jwt过期时间(小时)* @param claims 设置的信息* @return*/public static String createJWT(String secretKey , int dateOffset, Map<String, Object> claims) {// 指定签名的时候使用的签名算法,也就是header那部分SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;// 设置jwt的bodyJwtBuilder builder = Jwts.builder()// 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的.setClaims(claims)// 设置签名使用的签名算法和签名使用的秘钥.signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))// 设置过期时间.setExpiration(DateUtil.offset(new Date(), DateField.HOUR_OF_DAY, dateOffset));return builder.compact();}/*** Token解密** @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个* @param token 加密后的token* @return*/public static Claims parseJWT(String secretKey, String token) {try {// 得到DefaultJwtParserClaims claims = Jwts.parser()// 设置签名的秘钥.setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))// 设置需要解析的jwt.parseClaimsJws(token).getBody();return claims;} catch (Exception e) {
// throw new AccessDeniedException("没有权限,请登录");throw new RuntimeException("没有权限,请登录");}}}
权限管理核心配置类
@EnableWebSecurity
@Configuration
public class SecurityConfig {@AutowiredJwtAuthorizationManager jwtAuthorizationManager;@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests().antMatchers("/security/login").permitAll().antMatchers("/doc.html", "/webjars/**", "/swagger-resources/**", "/v2/api-docs").permitAll().anyRequest().access(jwtAuthorizationManager);http.csrf().disable();http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS );//关闭sessionhttp.headers().cacheControl().disable();//关闭缓存return http.build();}@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {return authenticationConfiguration.getAuthenticationManager();}/*** BCrypt密码编码* @return*/@Beanpublic BCryptPasswordEncoder bcryptPasswordEncoder() {return new BCryptPasswordEncoder();}}
登录接口
/*** 登录接口*/
@RestController
@Api(tags = "用户登录")
@RequestMapping("security")
public class LoginController {@AutowiredILoginService loginService;@PostMapping("/login")@ApiOperation(value = "用户登录",notes = "用户登录")@ApiImplicitParam(name = "userDto",value = "登录对象",required = true,dataType = "UserDto")public Result<UserVo> login(@RequestBody UserDto userDto){return Result.success(loginService.login(userDto));}
}
LoginServiceImpl
@Service
public class LoginServiceImpl implements ILoginService {@AutowiredAuthenticationManager authenticationManager;@Autowiredprivate JwtTokenManagerProperties jwtTokenManagerProperties;@Autowiredprivate StringRedisTemplate redisTemplate;@Overridepublic UserVo login(UserDto userDto) {UsernamePasswordAuthenticationToken authentication =new UsernamePasswordAuthenticationToken(userDto.getUsername(), userDto.getPassword());Authentication authenticate = authenticationManager.authenticate(authentication);UserAuth userAuth=(UserAuth)authenticate.getPrincipal();userAuth.setPassword("");UserVo userVoResult= BeanUtil.copyProperties(userAuth, UserVo.class);//userToken令牌颁布String userToken = UUID.randomUUID().toString();userVoResult.setUserToken(userToken);//构建载荷Map<String, Object> claims = new HashMap<>();String userVoJsonString = JSONObject.toJSONString(userVoResult);claims.put("currentUser", userVoJsonString);//jwtToken令牌颁布String jwtToken = JwtUtil.createJWT(jwtTokenManagerProperties.getBase64EncodedSecretKey(), jwtTokenManagerProperties.getTtl(), claims);//剔除缓存:用户关联userTokenString userTokenKey = UserCacheConstant.USER_TOKEN + userDto.getUsername();long ttl = Long.valueOf(jwtTokenManagerProperties.getTtl()) / 1000;//key:username value:uuidredisTemplate.opsForValue().set(userTokenKey, userToken, ttl, TimeUnit.SECONDS);//续期缓存:userToken关联jwtTokenString jwtTokenKey = UserCacheConstant.JWT_TOKEN + userToken;//key:uuid value:jwttokenredisTemplate.opsForValue().set(jwtTokenKey, jwtToken, ttl, TimeUnit.SECONDS);return userVoResult;}
}
JwtAuthorizationManager
@Slf4j
@Component
public class JwtAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {private AntPathMatcher antPathMatcher = new AntPathMatcher();@Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate JwtTokenManagerProperties jwtTokenManagerProperties;@Overridepublic AuthorizationDecision check(Supplier<Authentication> authentication,RequestAuthorizationContext requestAuthorizationContext) {//用户当前请求路径String method = requestAuthorizationContext.getRequest().getMethod();String requestURI = requestAuthorizationContext.getRequest().getRequestURI();String targetUrl = (method+requestURI);//获得请求中的认证后传递过来的userTokenString userToken = requestAuthorizationContext.getRequest().getHeader("authorization");//如果userToken为空,则当前请求不合法if (EmptyUtil.isNullOrEmpty(userToken)){return new AuthorizationDecision(false);}//通过userToken获取jwtTokenString jwtTokenKey = UserCacheConstant.JWT_TOKEN+userToken;//key:uuidString jwtToken = redisTemplate.opsForValue().get(jwtTokenKey);//如果jwtToken为空,则当前请求不合法if (EmptyUtil.isNullOrEmpty(jwtToken)){return new AuthorizationDecision(false);}//校验jwtToken是否合法Claims cla = JwtUtil.parseJWT(jwtTokenManagerProperties.getBase64EncodedSecretKey(), jwtToken);if (ObjectUtil.isEmpty(cla)) {//token失效return new AuthorizationDecision(false);}//如果校验jwtToken通过,则获得userVo对象UserVo userVo = JSONObject.parseObject(String.valueOf(cla.get("currentUser")),UserVo.class);//用户剔除校验:redis中最新的userToken与出入的userToken不符合,则认为当前用户被后续用户剔除//key:username value:uuidString currentUserToken = redisTemplate.opsForValue().get(UserCacheConstant.USER_TOKEN + userVo.getUsername());if (!userToken.equals(currentUserToken)){return new AuthorizationDecision(false);}//如果当前UserToken存活时间少于10分钟,则进行续期Long remainTimeToLive = redisTemplate.opsForValue().getOperations().getExpire(jwtTokenKey);if (remainTimeToLive.longValue()<= 600){//jwt生成的token也会过期,所以需要重新生成jwttokenMap<String, Object> claims = new HashMap<>();String userVoJsonString = String.valueOf(cla.get("currentUser"));claims.put("currentUser", userVoJsonString);//jwtToken令牌颁布String newJwtToken = JwtUtil.createJWT(jwtTokenManagerProperties.getBase64EncodedSecretKey(), jwtTokenManagerProperties.getTtl(), claims);long ttl = Long.valueOf(jwtTokenManagerProperties.getTtl()) / 1000;redisTemplate.opsForValue().set(jwtTokenKey, newJwtToken, ttl, TimeUnit.SECONDS);redisTemplate.expire(UserCacheConstant.USER_TOKEN + userVo.getUsername(), ttl, TimeUnit.SECONDS);}//TODO 判断当前用户是否拥有当前URL权限 以及见表(不太想写好麻烦啊,好累)//当前用户资源是否包含当前URL/*for (String resourceRequestPath : userVo.getResourceRequestPaths()) {boolean isMatch = antPathMatcher.match(resourceRequestPath, targetUrl);if (isMatch){log.info("用户:{}拥有targetUrl权限:{}==========",userVo.getUsername(),targetUrl);return new AuthorizationDecision(true);}return new AuthorizationDecision(false);}*/return new AuthorizationDecision(true);}}