当前位置: 首页 > news >正文

OpenAPI鉴权(二)jwt鉴权

一、思路

前端调用后端可以使用jwt鉴权;调用三方接口也可以使用jwt鉴权。对接多个三方则与每个third parth都约定一套token规则,因为如果使用同一套token,token串用可能造成权限越界问题,且payload交叉业务不够清晰。下面的demo包含了两套jwt,前端和一个三方(openApi)的:

1、token生成:

(1)签发给前端的token在本项目生成;

(2)签发给第三方的token,由第三方根据双方约定的算法、密钥和payload通信信息自己生成

      。不能调用本项目(被调用方)接口生成,否则这个生成token的接口需要加白名单,

      会造成接口攻击和token泄露的安全问题。

2、token校验:

   先判断是哪个业务的token,再用各自约定的算法和业务规则校验。这里是根据url来判断的,

   不能只根据audience来判断,如用前端的token访问open api接口,从token解析出audience

    是前端的,再用前端的规则校验,校验通过访问成功;

二、demo

1、pom与配置文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>openapi-ta</artifactId><groupId>us.zoom.openapi</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>openapi-ta-mas</artifactId><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><ta.product.name>Mas</ta.product.name></properties><dependencies><dependency><groupId>us.zoom.openapi</groupId><artifactId>openapi-ta-commons</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>us.zoom.openapi.mas</groupId><artifactId>open-api-mas-common</artifactId><version>1.0.1</version></dependency><dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.1.8</version></dependency><dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId><version>2.13.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding><skip>true</skip><executable>false</executable></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>${surefire.version}</version><configuration><systemPropertyVariables><org.uncommons.reportng.escape-output>false</org.uncommons.reportng.escape-output><spring.profiles.active>${spring.profiles.active}</spring.profiles.active><ta.product.name>${ta.product.name}</ta.product.name></systemPropertyVariables><skip>false</skip><testFailureIgnore>false</testFailureIgnore><argLine>-javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"--add-opens java.base/java.lang=ALL-UNNAMED</argLine><suiteXmlFiles><suiteXmlFile>${project.basedir}/src/main/resources/suites/${spring.profiles.active}/AlertRuleAPIBvtSuite.xml</suiteXmlFile></suiteXmlFiles><properties><usedefaultlisteners>false</usedefaultlisteners></properties></configuration></plugin></plugins><resources><resource><directory>src/main/resources</directory><filtering>true</filtering><includes><include>**/*.properties</include><include>**/*.xml</include><include>application.yml</include><include>application-${spring.profiles.active}.yml</include></includes></resource><resource><directory>src/main/java</directory><filtering>false</filtering></resource></resources></build><profiles><profile><id>dev</id><activation><!--default env--><activeByDefault>true</activeByDefault></activation><properties><spring.profiles.active>dev</spring.profiles.active></properties></profile><profile><id>go</id><properties><spring.profiles.active>go</spring.profiles.active></properties></profile><profile><id>aw1</id><properties><spring.profiles.active>aw1</spring.profiles.active></properties></profile><profile><id>us02</id><properties><spring.profiles.active>us02</spring.profiles.active></properties></profile><profile><id>us03</id><properties><spring.profiles.active>us03</spring.profiles.active></properties></profile><profile><id>us04</id><properties><spring.profiles.active>us04</spring.profiles.active></properties></profile><profile><id>us05</id><properties><spring.profiles.active>us05</spring.profiles.active></properties></profile><profile><id>us06</id><properties><spring.profiles.active>us06</spring.profiles.active></properties></profile><profile><id>us07</id><properties><spring.profiles.active>us07</spring.profiles.active></properties></profile><profile><id>us01</id><properties><spring.profiles.active>us01</spring.profiles.active></properties></profile></profiles></project>
server.port=6666
server.servlet.context-path=/jwtDemo
2、启动类 
package com.demo.security;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableMBeanExport;
import org.springframework.jmx.support.RegistrationPolicy;@SpringBootApplication
//解决报错MXBean already registered with name org.apache.commons.pool2:type=GenericObjectPool,name=pool
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
public class JWTApplication {public static void main(String[] args) {SpringApplication.run(JWTApplication.class, args);}
}
 3、全局配置

(1)webMVC

package com.demo.security.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebMvcConfig implements WebMvcConfigurer {//设置跨域@Beanpublic CorsFilter corsFilter() {UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();CorsConfiguration config = new CorsConfiguration();config.setAllowCredentials(true);config.addAllowedOrigin("http://localhost:9528");config.addAllowedHeader("*");config.addAllowedMethod("*");source.registerCorsConfiguration("/**", config);return new CorsFilter(source);}
}

(2)security

package com.demo.security.config;import org.springframework.security.crypto.password.PasswordEncoder;public class MyPasswordEncoder implements PasswordEncoder {@Overridepublic String encode(CharSequence charSequence) {return charSequence.toString();}@Overridepublic boolean matches(CharSequence charSequence, String s) {return s.equals(charSequence.toString());}/*public DefaultPasswordEncoder() {this(-1);}*//*** @param strength*            the log rounds to use, between 4 and 31*//*public DefaultPasswordEncoder(int strength) {}public String encode(CharSequence rawPassword) {return MD5.encrypt(rawPassword.toString());}public boolean matches(CharSequence rawPassword, String encodedPassword) {return encodedPassword.equals(MD5.encrypt(rawPassword.toString()));}*/
}

package com.demo.security.config;import com.demo.security.filter.LoginFilter;
import com.demo.security.filter.TokenAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityWebConfig {@Autowiredprivate UserDetailsService userDetailsService;@Beanpublic PasswordEncoder passwordEncoder() {return new MyPasswordEncoder();}@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {return authConfig.getAuthenticationManager();}@Beanpublic SecurityFilterChain configure(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {http.csrf(AbstractHttpConfigurer::disable);http.headers(AbstractHttpConfigurer::disable);http.sessionManagement(sessionManagement -> {sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS);});http.authorizeRequests().anyRequest().authenticated().and()//1、登陆、退出url,均由前端拦截器控制,这里注释掉。//1.1、前端拦截器中判断缓存token为空,为空则post请求访问/login,目的是进入LoginFilter获取token//1.2、不为空则带token访问接口,如果AuthenticationFilter拦截token不合法则根据错误码跳转到登陆页面,重复1.1的操作//.logout().logoutUrl("/logout").and()//2、身份认证filter,访问系统(除了白名单接口)需要先登陆。post请求/login接口会进入这个拦截器// 校验用户名密码是否正确,正确返回token给前端,不正确则返回异常信息.addFilterBefore(new LoginFilter(authenticationManager), LoginFilter.class)//3、授权filer,authenticationManager为BasicAuthenticationFilter的必传参数。所有的接口都会走到这里// 根据用户id查询权限,连同身份一起塞入SecurityContextHolder全局变量,后面获取用户信息则直接从SecurityContextHolder中get.addFilterBefore(new TokenAuthenticationFilter(authenticationManager,userDetailsService),TokenAuthenticationFilter.class);return http.build();}@Beanpublic WebSecurityCustomizer webSecurityCustomizer() {return (web) -> web.ignoring().requestMatchers("/param/**", "/user-websocket-endpoint/**","/menu-websocket-endpoint/**");}
}
4、security数据源

(1)常量(模拟数据库)

package com.demo.security.constant;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 模拟数据库查询数据,假设有:用户名/密码/角色/资源* admin/123/xtgly/user_manage、role_manage、menu_manage、school_manage* zs/123/userAdmin、roleAdmin/user_manage、role_manage、menu_manage* ls/123/schoolAdmin/school_manage*/
public class UserDBConstants {public static Map<String, String> getUsers() {Map<String,String> users = new HashMap<>();users.put("admin","123");users.put("zs","123");users.put("ls","123");return users;}public static Map<String,List<String>> getUserRoles() {Map<String,List<String>> userRoles = new HashMap<>();//adminList<String> adminRoles = new ArrayList<>();adminRoles.add("xtgly");userRoles.put("admin",adminRoles);//zsList<String> zsRoles = new ArrayList<>();zsRoles.add("userAdmin");zsRoles.add("roleAdmin");userRoles.put("zs",zsRoles);//lsList<String> lsRoles = new ArrayList<>();lsRoles.add("schoolAdmin");userRoles.put("ls",lsRoles);return userRoles;}public static Map<String,List<String>> getUserPermissions() {Map<String,List<String>> userPermissions = new HashMap<>();List<String> lsPermissions = new ArrayList<>();//lslsPermissions.add("school_manage");userPermissions.put("ls",lsPermissions);//zsList<String> zsPermissions = new ArrayList<>();zsPermissions.add("user_manage");zsPermissions.add("role_manage");zsPermissions.add("menu_manage");userPermissions.put("zs",zsPermissions);//adminList<String> adminPermissions = new ArrayList<>();adminPermissions.add("user_manage");adminPermissions.add("role_manage");adminPermissions.add("menu_manage");adminPermissions.add("school_manage");userPermissions.put("admin",adminPermissions);return userPermissions;}
}

(2)UserDetails

package com.demo.security.dto;import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection;
import java.util.List;
@Data
public class UserDTO implements UserDetails {private Integer id;private String userName;private String userAccount;private List<String> roles;private List<String> menus;private String passWord;public UserDTO (Integer id,String userName,String userAccount,List<String> roles,List<String> menus,String passWord){this.id = id;this.userAccount = userAccount;this.userName = userName;this.roles = roles;this.menus = menus;this.passWord = passWord;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}public List<String> getMenus() {return menus;}public void setMenus(List<String> menus) {this.menus = menus;}@Overridepublic String getPassword() {return passWord;}@Overridepublic String getUsername() {return this.userAccount;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

(3)UserDetailService

package com.demo.security.service;import com.demo.security.constant.UserDBConstants;
import com.demo.security.dto.UserDTO;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import java.util.List;
import java.util.Map;/*** 当什么也没有配置的时候,账号和密码是由 Spring Security 定义生成的。* 而在实际项目中账号和密码都是从数据库中查询出来的。所以我们要通过自定义逻辑控制认证逻辑,*/
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//模拟数据库查询Map<String, String> userMap = UserDBConstants.getUsers();String dbPwd = userMap.get(username);if(StringUtils.isEmpty(dbPwd)){throw new UsernameNotFoundException("用户不存在");}Map<String, List<String>> userRoles = UserDBConstants.getUserRoles();List<String> roles = userRoles.get(username);Map<String, List<String>> userMenus = UserDBConstants.getUserPermissions();List<String> menus = userMenus.get(username);UserDTO userDTO = new UserDTO(null,null,username,roles,menus,dbPwd);return userDTO;}
}
 5、filter

(1)前端登录

package com.demo.security.filter;import com.demo.security.constant.UserDBConstants;
import com.demo.security.dto.ResponseMsg;
import com.demo.security.dto.UserDTO;
import com.demo.security.util.JwtUtil;
import com.google.gson.Gson;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.entity.ContentType;
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.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;@Slf4j
public class LoginFilter extends UsernamePasswordAuthenticationFilter {private AuthenticationManager authenticationManager;public LoginFilter(AuthenticationManager authenticationManager) {//super(new AntPathRequestMatcher("/login", "POST"));this.authenticationManager = authenticationManager;}/*** /login POST接口验证* @param req* @param res* @return* @throws AuthenticationException*/@Overridepublic Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException {try {logger.info("进入LoginFilter");String userName = req.getParameter("userName");String passWord = req.getParameter("passWord");if (StringUtils.isEmpty(userName)) {throw new UsernameNotFoundException("请输入账号");}if (StringUtils.isEmpty(passWord)) {throw new UsernameNotFoundException("请输入密码");}//验证用户名密码是否正确Map<String, String> userMap = UserDBConstants.getUsers();if(!userMap.keySet().contains(userName)){throw new UsernameNotFoundException("用户不存在");}if(!passWord.equals(userMap.get(userName))){throw new UsernameNotFoundException("密码错误");}//这里权限返回空,由后面的授权过滤器查询return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userName, passWord, new ArrayList<>()));} catch (UsernameNotFoundException e) {//返回错误信息res.setCharacterEncoding("UTF-8");res.setContentType("application/text;charset=utf-8");try {res.getWriter().write(e.getMessage());} catch (IOException e1) {e1.printStackTrace();}return null;}catch (Exception e){throw new RuntimeException(e);}}@Overrideprotected void successfulAuthentication(HttpServletRequest request,HttpServletResponse res,jakarta.servlet.FilterChain chain,Authentication authResult)throws IOException{UserDTO userDTO = (UserDTO) authResult.getPrincipal();String jwtToken = JwtUtil.generateWebToken(userDTO);//返ResponseMsg resMsg = ResponseMsg.builder().code(200).data(jwtToken).build();res.setContentType(ContentType.TEXT_HTML.toString());Gson gson = new Gson();res.getWriter().write(gson.toJson(resMsg));}
}

(2)token验证

package com.demo.security.filter;import com.demo.security.util.JwtUtil;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
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.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;import java.io.IOException;@Slf4j
public class TokenAuthenticationFilter extends BasicAuthenticationFilter {private UserDetailsService userDetailsService;public TokenAuthenticationFilter(AuthenticationManager authenticationManager, UserDetailsService userDetailsService) {super(authenticationManager);this.userDetailsService = userDetailsService;}@Overrideprotected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {logger.info("登陆成功后访问,url={}"+req.getRequestURI());String token = req.getHeader("token");res.setCharacterEncoding("UTF-8");res.setContentType("application/text;charset=utf-8");//1、必填tokenif(StringUtils.isEmpty(token)){logger.info("登陆成功后访问,url={},token为空"+req.getRequestURI());res.getWriter().write("token为空");return;}//2、校验token是否合法,合法则解析出userName//token可能是前端的,也可能是open api的String userName = JwtUtil.getUserNameByToken(req,token);if(StringUtils.isEmpty(userName)){logger.info("登陆成功后访问,url={},token错误或失效"+req.getRequestURI());res.getWriter().write("token错误或者失效");return;}//3、根据userName获取user实体,存入全局UserDetails currentUser = userDetailsService.loadUserByUsername(userName);UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(currentUser,null,currentUser.getAuthorities());SecurityContextHolder.getContext().setAuthentication(authentication);chain.doFilter(req, res);}
}
 6、一些dto和常量
package com.demo.security.dto;import lombok.Builder;
import lombok.Data;@Data
@Builder
public class ResponseMsg {private Object data;private Integer code;
}
package com.demo.security.constant;import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import java.util.List;public class UrlConstants {public static final String WEB_API_PRE = "/v1/**";public static final String OPEN_API_PRE = "/openApi/**";public static final List<AntPathRequestMatcher> WEB_MATCHERS =  List.of(new AntPathRequestMatcher(WEB_API_PRE));public static final List<AntPathRequestMatcher> OPEN_API_MATCHERS =List.of(new AntPathRequestMatcher(OPEN_API_PRE));
}
 7、JWTUtil
package com.demo.security.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.demo.security.constant.UrlConstants;
import com.demo.security.dto.UserDTO;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;@Slf4j
public class JwtUtil {//签发方private static final String APPLICATION_ISSUER = "jwt_demo";//签发给private static final String TO_WEB = "to_web";private static final String TO_OPEN_API = "to_open_api";//密钥private static final String WEB_SECRET = "web-secret";private static final String OPEN_API_SECRET = "open-api-secret";//jwt过期事件public static final int EXPIRE_TIME = 30 * 60 * 1000;/*** 生成给前端的签名* @return 加密的token*/public static String generateWebToken(UserDTO userDTO) {Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);Algorithm algorithm = Algorithm.HMAC256(WEB_SECRET);// 附带username信息return JWT.create()//iss:签发方.withIssuer(APPLICATION_ISSUER)//aud:接收jwt的一方.withAudience(TO_WEB)//exp:jwt的过期时间,这个过期时间必须要大于签发时间//.withExpiresAt()//其他自定义通信信息.withClaim("userName", userDTO.getUsername())//.withClaim("age",userDTO.getAge());.withExpiresAt(date).sign(algorithm);}/*** open api的签名生成* 此处是给单元测试用的,调用方项目应该自己生成* @return 加密的token*/public static String generateOpenApiToken(String userName) {Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);Algorithm algorithm = Algorithm.HMAC256(OPEN_API_SECRET);// 附带username信息return JWT.create()//iss:签发方.withIssuer(APPLICATION_ISSUER)//aud:接收jwt的一方.withAudience(TO_OPEN_API)//exp:jwt的过期时间,这个过期时间必须要大于签发时间//.withExpiresAt()//其他自定义通信信息.withClaim("userName", userName)//.withClaim("age",age);.withExpiresAt(date).sign(algorithm);}public static String getUserNameByToken(HttpServletRequest req, String token) {DecodedJWT jwt = JWT.decode(token);//1、是否过期Date expireDate = jwt.getExpiresAt();if(expireDate.before(new Date())){log.error("token已过期");return null;}//2、签发方是否正确if(!APPLICATION_ISSUER.equals(jwt.getIssuer())){log.error("不是本项目签发的token");return null;}//3、是否合法String audience = jwt.getAudience().get(0);boolean check = preCheckToken(audience,token,req);if(!check){return null;}Map<String, Claim> claims = jwt.getClaims();String userName = claims.get("userName").asString();return userName;}/*** 根据url来判断是web还是open api更准确,* 如果只根据audience来判断,则web的token也可以访问open api;** @param audience* @param token* @param req* @return*/private static boolean preCheckToken(String audience, String token, HttpServletRequest req) {if(TO_WEB.equals(audience) && urlMatches(req, UrlConstants.WEB_MATCHERS)){log.info("这是前端token");return verifyToken(WEB_SECRET,token);}else if(TO_OPEN_API.equals(audience) && urlMatches(req,UrlConstants.OPEN_API_MATCHERS)){log.info("这是open api token");return verifyToken(OPEN_API_SECRET,token);}log.error("token来源不合法");return false;}public static boolean urlMatches(HttpServletRequest request, List<AntPathRequestMatcher> matchers) {Optional<AntPathRequestMatcher> first = matchers.stream().filter(m -> m.matches(request)).findFirst();return first.isPresent();}private static boolean verifyToken(String secret, String token) {Algorithm algorithm = Algorithm.HMAC256(secret);try{JWTVerifier verifier = JWT.require(algorithm).build();verifier.verify(token);return true;}catch (Exception e){log.error("token非法");return false;}}
}
8、checkUtil
package com.demo.security.check;import com.demo.security.dto.UserDTO;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;import java.awt.*;
import java.util.List;@Component("menuAuthorizationCheck")
public class MenuAuthorizationCheck {public boolean hasMenuAuthorization(String menuCode) {UserDTO currentUser = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();List<String> menus = currentUser.getMenus();return menus.contains(menuCode);}/* *//*** open api是否有权限访问* @param menuCode* @return*//*public boolean hasOpenMenuAuthorization(String menuCode) {UserDTO currentUser = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();List<String> menus = currentUser.getMenus();return menus.contains(menuCode);}*/
}
 9、controller

(1)前端业务接口

package com.demo.security.controller;import com.demo.security.dto.ResponseMsg;
import com.demo.security.dto.UserDTO;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/v1/user")
@RestController
public class UserController {@RequestMapping("/test")public String test(){return "这是user test";}@RequestMapping("/getCurrentUser")public ResponseMsg getCurrentUser() {UserDTO currentUser = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();System.out.println("当前用户为:"+currentUser);return ResponseMsg.builder().code(200).data(currentUser).build();}
}
package com.demo.security.controller;import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/v1/menu")
@RestController
public class MenuManageController {@PreAuthorize("@menuAuthorizationCheck.hasMenuAuthorization('menu_manage')")@RequestMapping("/test")public String test(){return "这是菜单管理";}
}

package com.demo.security.controller;import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/v1/school")
@RestController
@Slf4j
public class SchoolManageController {@RequestMapping("/test")public String test(){log.info("这是学校管理controller");return "这是学校管理";}
}
package com.demo.security.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/v1/role")
@RestController
public class RoleManageController {@RequestMapping("/test")public String test(){return "这是角色管理";}
}

(2)open api业务接口

package com.demo.security.openapi;import com.demo.security.dto.ResponseMsg;
import com.demo.security.dto.UserDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/openApi/user")
@RestController
public class OpenUserController {@RequestMapping("/test")public String test(){return "这是open api user test";}@RequestMapping("/getCurrentUser")public ResponseMsg getCurrentUser() {UserDTO currentUser = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();System.out.println("当前用户为:"+currentUser);return ResponseMsg.builder().code(200).data(currentUser).build();}
}

package com.demo.security.openapi;import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/openApi/menu")
@RestController
public class OpenMenuController {@PreAuthorize("@menuAuthorizationCheck.hasMenuAuthorization('menu_manage')")@RequestMapping("/test")public String test(){return "这是open api菜单管理";}
}
package com.demo.security.openapi;import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/openApi/school")
@RestController
@Slf4j
public class OpenSchoolController {@RequestMapping("/test")public String test(){log.info("这是学校管理controller");return "这是open api学校管理";}
}

测试验证
(1)模拟前端调用

① 登录,访问 localhost:6666/jwtDemo/login?userName=admin&passWord=123

     

使用返回的token调用业务接口:

② 访问 localhost:6666/jwtDemo/v1/user/test

③ 访问localhost:6666/jwtDemo/v1/user/getCurrentUser

④ 访问localhost:6666/jwtDemo/v1/menu/test

⑤ 访问 localhost:6666/jwtDemo/v1/school/test

(2)模拟openapi调用

① 使用单元测试生成一个token

package com.demo.jwt.openapi;import com.demo.security.JWTApplication;
import com.demo.security.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;@SpringBootTest(classes = {JWTApplication.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
@Slf4j
public class MyTest {/*** 模拟open api调用方生成token。* open api应该自己生成token,双方约定好生成的算法、密钥和payload通信字段;* 不能调用本项目(被调用方)接口生成,否则对于生成token的接口需要加白名单,会造成接口攻击和token泄露的安全问题**/@Testpublic void  createOpenApiToken(){String userName = "zs";String token = JwtUtil.generateOpenApiToken(userName);log.info("生成token:{}",token);}}

使用这个token调用业务接口:

② 访问 localhost:6666/jwtDemo/openApi/user/getCurrentUser

③ 访问 localhost:6666/jwtDemo/openApi/school/test

④ 访问 localhost:6666/jwtDemo/openApi/menu/test

(3)前端使用openapi的token调用接口

都报错

(4)openapi使用前端的token调用接口

同样都报错

相关文章:

OpenAPI鉴权(二)jwt鉴权

一、思路 前端调用后端可以使用jwt鉴权&#xff1b;调用三方接口也可以使用jwt鉴权。对接多个三方则与每个third parth都约定一套token规则&#xff0c;因为如果使用同一套token&#xff0c;token串用可能造成权限越界问题&#xff0c;且payload交叉业务不够清晰。下面的demo包…...

【Rust练习】16.模式

文章题目来自&#xff1a;https://practice-zh.course.rs/pattern-match/patterns.html 1 &#x1f31f;&#x1f31f; 使用 | 可以匹配多个值, 而使用 … 可以匹配一个闭区间的数值序列 fn main() {} fn match_number(n: i32) {match n {// 匹配一个单独的值1 > println!(…...

深度学习(4):torch.nn.Module

文章目录 一、是什么二、nn.Module 的核心功能三、nn.Module 的基本用法1. 定义自定义模型2. 初始化模型3. 模型的使用 四、nn.Module 的关键特性1. 自动注册子模块和参数2. forward 方法3. 不需要定义反向传播 五、常用的内置模块六、示例&#xff1a;创建一个简单的神经网络1…...

(14)关于docker如何通过防火墙做策略限制

关于docker如何通过防火墙做策略限制 1、iptables相关问题 在Iptables防火墙中包含四种常见的表&#xff0c;分别是filter、nat、mangle、raw。 filter&#xff1a;负责过滤数据包。 filter表可以管理INPUT、OUTPUT、FORWARD链。 nat&#xff1a;用于网络地址转换。 nat表…...

新React开发人员应该如何思考

React是一个用于构建用户界面的流行JavaScript库&#xff0c;通过使开发人员能够创建可重用组件并有效管理复杂的UI&#xff0c;彻底改变了前端开发。然而&#xff0c;采用正确的心态对于新开发人员驾驭React独特的范式至关重要。让我们来探索塑造“React思维模式”的基本原则和…...

解密.bixi、.baxia勒索病毒:如何安全恢复被加密数据

导言 在数字化时代&#xff0c;数据安全已成为个人和企业面临的重大挑战之一。随着网络攻击手段的不断演进&#xff0c;勒索病毒的出现尤为引人关注。其中&#xff0c;.bixi、.baxia勒索病毒是一种新型的恶意软件&#xff0c;它通过加密用户的重要文件&#xff0c;迫使受害者支…...

开源 AI 智能名片与 S2B2C 商城小程序:嫁接权威实现信任与增长

摘要&#xff1a;本文探讨了嫁接权威在产品营销中的重要性&#xff0c;并结合开源 AI 智能名片与 S2B2C 商城小程序&#xff0c;阐述了如何通过与权威关联来建立客户信任&#xff0c;提升产品竞争力。强调了在当今商业环境中&#xff0c;巧妙运用嫁接权威的方法&#xff0c;能够…...

S-Clustr-Simple 飞机大战:骇入现实的建筑灯光游戏

项目地址:https://github.com/MartinxMax/S-Clustr/releases Video https://www.youtube.com/watch?vr3JIZY1olro 飞机大战 按键操作: ←:向左移动 →:向右移动 Space:发射子弹 这是一个影子集群的游戏插件&#xff0c;可以将游戏画面映射到现实的设备&#xff0c;允许恶…...

MySQL:存储引擎简介和库的基本操作

目录 一、存储引擎 1、什么是存储引擎&#xff1f; 2、存储引擎的分类 关系型数据库存储引擎&#xff1a; 非关系型数据库存储引擎&#xff1a; 分布式数据库存储引擎&#xff1a; 3、常用的存储引擎及优缺点 1、InnoDb存储引擎 2、MyISAM存储引擎 3、MEMORY存储引擎 …...

JavaScript类型判断(总结)

1. 使用typeof操作符 typeof操作符可以返回一个值的类型的字符串表示。例如&#xff1a; typeof 42; // "number" typeof "Hello"; // "string" typeof true; // "boolean" typeof undefined; // "undefined" typeof null…...

SpringBoot之登录校验关于JWT、Filter、interceptor、异常处理的使用

什么是登录校验&#xff1f; 所谓登录校验&#xff0c;指的是我们在服务器端接收到浏览器发送过来的请求之后&#xff0c;首先我们要对请求进行校验。先要校验一下用户登录了没有&#xff0c;如果用户已经登录了&#xff0c;就直接执行对应的业务操作就可以了&#xff1b;如果用…...

我的AI工具箱Tauri版-FunAsr音频转文本

本教程基于自研的AI工具箱Tauri版进行FunAsr音频转文本服务。 FunAsr音频转文本服务 是自研AI工具箱Tauri版中的一个高效模块&#xff0c;专为将音频或视频中的语音内容自动转化为文本或字幕而设计。用户只需简单配置输入、输出路径&#xff0c;即可通过FunAsr工具快速批量处理…...

C++:模版初阶

目录 一、泛型编程 二、函数模版 概念 格式 原理 实例化 模版参数的匹配原则 三、类模版 定义格式 实例化 一、泛型编程 如何实现一个通用的交换函数呢&#xff1f; void Swap(int& left, int& right) {int temp left;left right;right temp; } void Swa…...

Python Web 与区块链集成的最佳实践:智能合约、DApp与安全

Python Web 与区块链集成的最佳实践&#xff1a;智能合约、DApp与安全 &#x1f4da; 目录 &#x1f3d7; 区块链基础 区块链的基础概念与应用场景使用 Web3.py 与 Python Web 应用集成区块链网络在 Web 应用中实现加密货币支付与转账功能 &#x1f511; 智能合约与 DApp 编写…...

使用工具将截图公式转换为word公式

引言&#xff1a; 公式越复杂&#xff0c;心情越凌乱&#xff0c;手写都会觉得很麻烦&#xff0c;何况敲到电脑里面呢&#xff0c;特别是在写论文时&#xff0c;word有专属的公式格式&#xff0c;十分繁杂&#xff0c;如果照着mathTYPE软件敲&#xff0c;那么会耗费很长的时间…...

深度学习(6):Dataset 和 DataLoader

文章目录 Dataset 类DataLoader 类 Dataset 类 概念&#xff1a; Dataset 是一个抽象类&#xff0c;用于表示数据集。它定义了如何获取数据集中的单个样本和标签。 作用&#xff1a; 为数据集提供统一的接口&#xff0c;便于数据的读取、预处理和管理。 关键方法&#xff…...

Qt窗口——QToolBar

文章目录 工具栏创建工具栏设置toolTip工具栏配合菜单栏工具栏浮动状态 工具栏 QToolBar工具栏是应用程序中集成各种功能实现快捷键使用的一个区域。 可以有多个&#xff0c;也可以没有。 创建工具栏 #include "mainwindow.h" #include "ui_mainwindow.h&qu…...

MySQL—存储过程详解

基本介绍 存储过程和函数是数据库中预先编译并存储的一组SQL语句集合。它们的主要目的是提高代码的复用性、减少数据传输、简化业务逻辑处理&#xff0c;并且一旦编译成功&#xff0c;可以永久有效。 存储过程和函数的好处 提高代码的复用性&#xff1a;存储过程和函数可以在…...

2024ICPC网络赛2记录:CK

这一次网络赛我们过8题&#xff0c;排名71&#xff0c;算是发挥的非常好的了。这一把我们三个人手感都很好&#xff0c;前六题都是一遍过&#xff0c;然后我又切掉了非签到的E和C&#xff0c;最后时间不是很多&#xff0c;K只想到大概字典树的思路&#xff0c;细节不是很懂就直…...

PerparedStatement概述

PreparedStatement 是 Java 中的一个接口&#xff0c;用于预编译 SQL 语句并执行数据库操作。 一、主要作用 提高性能&#xff1a; 数据库在首次执行预编译语句时会进行语法分析、优化等操作&#xff0c;并将其存储在缓存中。后续执行相同的预编译语句时&#xff0c;数据库可…...

联影医疗嵌入式面试题及参考答案(3万字长文)

假如你要做机器人控制,你会遵循怎样的开发流程? 首先,需求分析阶段。明确机器人的功能需求,例如是用于工业生产中的物料搬运、还是家庭服务中的清洁打扫等。了解工作环境的特点,包括空间大小、障碍物分布、温度湿度等因素。同时,确定机器人的性能指标,如运动速度、精度、…...

Rust的作用?

在Linux中&#xff0c;Rust可以开发命令行工具&#xff0c;如FD、SD、Ripgep、Bat、EXA、SKIM等。虽然Rust是面向系统编程&#xff0c;但也不妨碍使用Rust写命令行工具&#xff0c;因为Rust具备现代语言特性、无依赖、生成的目标文件小。 在云计算和区块链区域&#xff0c;Rus…...

无人机之可承受风速的影响因素

无人机可承受风速的影响因素是多方面的&#xff0c;这些因素共同决定了无人机在特定风速条件下的飞行稳定性和安全性。以下是一些主要的影响因素&#xff1a; 一、无人机设计与结构 无人机的大小、形状和重量都会直接影响其抗风能力。大型无人机由于具有更大的表面积和质量&am…...

HTML与JavaScript结合实现简易计算器

目录 背景&#xff1a; 过程&#xff1a; 代码: HTML部分解析&#xff1a; body部分解析&#xff1a; JavaScript部分解析&#xff1a; 效果图 &#xff1a; 总结: 背景&#xff1a; 计算器是一个典型的HTML和javaScript结合使用的例子&#xff0c;它展示了如何使用H…...

Docker网络原理

Docker 网络是 Docker 容器之间以及容器与外部世界之间通信的机制。Docker 提供了多种网络驱动&#xff0c;允许容器以不同的方式进行通信&#xff1a; Docker 网络工作原理&#xff1a; 网络命名空间&#xff1a;Docker 使用 Linux 的网络命名空间来隔离容器的网络堆栈。每个…...

PyTorch 目标检测教程

PyTorch 目标检测教程 本教程将介绍如何在 PyTorch 中使用几种常见的目标检测模型&#xff0c;包括 Faster R-CNN、SSD 以及 YOLO (You Only Look Once)。我们将涵盖预训练模型的使用、推理、微调&#xff0c;以及自定义数据集上的训练。 1. 目标检测概述 目标检测任务不仅要…...

校园美食导航:Spring Boot技术的美食发现之旅

第二章 系统分析 2.1 可行性分析 可行性分析的目的是确定一个系统是否有必要开发、确定系统是否能以最小的代价实现。其工作主要有三个方面&#xff0c;分别是技术、经济和社会三方面的可行性。我会从这三个方面对网上校园周边美食探索及分享平台进行详细的分析。 2.1.1技术可行…...

51单片机 - DS18B20实验1-读取温度

上来一张图&#xff0c;明确思路&#xff0c;程序整体裤架如下&#xff0c;通过单总线&#xff0c;单独封装一个.c文件用于单总线的操作&#xff0c;其实&#xff0c;我们可以把点c文件看成一个类操作&#xff0c;其属性就是我们面向对象的函数&#xff0c;也叫方法&#xff0c…...

go语言基础入门(一)

变量声明:批量声明变量:变量赋值: 声明变量同时为变量赋值可以在变量声明时为其赋值go中赋值时的编译器会自动根据等号右侧的数据类型自动推导变量的类型使用 : 进行赋值匿名变量 常量常量计数器iota1. 使用场景2. 基本用法3. 简化语法4. 自定义增量5. 复杂使用go的类似枚举 使…...

linux 基础(一)mkdir、ls、vi、ifconfig

1、linux简介 linux是一个操作系统&#xff08;os: operating system&#xff09; 中国有没有自己的操作系统&#xff08;华为鸿蒙HarmonyOS&#xff0c;阿里龙蜥(Anolis) OS 8、百度DuerOS都有&#xff09; 计算机组的组成&#xff1a;硬件软件 硬件&#xff1a;运算器&am…...