首页 > 其他分享 >记录springboot 3.3.5 版本整合 swagger +spring security + jwt

记录springboot 3.3.5 版本整合 swagger +spring security + jwt

时间:2024-10-31 17:13:42浏览次数:1  
标签:return springboot spring jwt token new swagger userDetail public

springboot 版本
security 版本
wagger 版本
jwt 版本
redis 版本 pom文件如下
引入redis 是为了存储 token

<version>3.3.5</version>

<!--security-->  
<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-security</artifactId>  
    <version>3.1.8</version>  
</dependency>


<!--swagger-->  
<dependency>  
    <groupId>org.springdoc</groupId>  
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>  
    <version>2.6.0</version>  
</dependency>  
  
  
<!--jwt-->  
<dependency>  
    <groupId>io.jsonwebtoken</groupId>  
    <artifactId>jjwt</artifactId>  
    <version>0.9.1</version>  
</dependency>  
<!-- 如果jdk大于1.8,则还需导入下面依赖-->  
<dependency>  
    <groupId>javax.xml.bind</groupId>  
    <artifactId>jaxb-api</artifactId>  
    <version>2.3.1</version>  
</dependency>  
<!--jwt-->

<!-- Spring Data Redis -->  
<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-data-redis</artifactId>  
</dependency>  
<!-- Jedis -->  
<dependency>  
    <groupId>redis.clients</groupId>  
    <artifactId>jedis</artifactId>  
</dependency>

yaml文件的配置如下

server:  
  port: 8586           #端口号  
  servlet:  
    context-path: /api    #接口统一自带前缀 (也就是因为这个配置很多地方需要改动)
spring:  
  application:  
    name: demoServe    # 名字  
  datasource:          #数据库  
    driver-class-name: com.mysql.jdbc.Driver  
    url: jdbc:mysql://xx.xx.xx.xx:xx/xx?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useUnicode=true  
    username: xxx    #数据库用户
    password: xxxx   #密码
  
  data:  
    redis:  
      database: x    #换成自己要用的库 例  1
      host: xx.xx.xx.xx   #换成自己的地址 
      port: xx        #自己的端口
      password:  
      pool:  
        max-idle: 8  
        min-idle: 0  
        max-active: 8  
        max-wait: 8  
      timeout: 5000  
springdoc:  
  api-docs:  
    path: /v3/api-docs  
    group: default  
    enabled: true  
  swagger-ui:                 #去掉 url 和 config-url 参数 可以正常访问 加上后需要配置正确路径 (暂时不理解)不然报404错误。  
    path: /swagger-ui.html  
    #base-url: /api  
    enabled: true

创建 swagger 的配置文件 SwaggerConfig

@Configuration  
public class SwaggerConfig {  
    @Bean  
    public OpenAPI openAPI() {  
        return new OpenAPI()  
                .info(new Info()  
                        .title("demo接口文档")  
                        .description("SpringBoot3 集成 Swagger3接口文档")  
                        .version("v1"))  
                .externalDocs(new ExternalDocumentation()  
                        .description("项目API文档")  
                        .url("/"));  
    }

创建 security 的配置文件 如下

@Configuration  
@EnableWebSecurity  
@EnableGlobalMethodSecurity(prePostEnabled = true)  
public class NewWebSecurityConfig {  
  
    @Autowired  
    UserNameAuthenticationProvider userNameAuthenticationProvider;   //是用security代理的登录
  
    private final JwtAuthenticationEntryPoint unauthorizedHandler;   //未授权处理逻辑  
    private final RestAuthenticationAccessDeniedHandler accessDeniedHandler;  //权限不足  
    private final JwtAuthenticationTokenFilter authenticationTokenFilter;  //全局访问统一入口  
  
    public NewWebSecurityConfig(JwtAuthenticationEntryPoint unauthorizedHandler, RestAuthenticationAccessDeniedHandler accessDeniedHandler, JwtAuthenticationTokenFilter authenticationTokenFilter) {  
        this.unauthorizedHandler = unauthorizedHandler;  
        this.accessDeniedHandler = accessDeniedHandler;  
        this.authenticationTokenFilter = authenticationTokenFilter;  
    }  
  
    // 获取AuthenticationManager(认证管理器),登录时认证使用  
    @Bean  
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {  
        return authenticationConfiguration.getAuthenticationManager();  
    }  
  
    @Bean  
    SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {  
  
        httpSecurity  
                .authorizeHttpRequests(authorize -> authorize  
                        .requestMatchers("/","login/token","/api/login.token","/login/token").permitAll() // 放行的接口  
                        .requestMatchers("/api/swagger-ui/**", "/api/*/api-docs/**").permitAll()  // 允许未登录用户访问 Swagger UI                        .anyRequest().authenticated()  
                )  
                .sessionManagement(session -> session  
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)  
                )  
                .csrf(AbstractHttpConfigurer::disable)   // 禁用
                .cors(cors -> cors.disable())  // 使用具体的实现对象  (这里我一直配不好 所以选择了禁用) 
                .exceptionHandling(exception -> exception  
                        .authenticationEntryPoint(unauthorizedHandler)  
                        .accessDeniedHandler(accessDeniedHandler)  
                )  
                .authenticationProvider(userNameAuthenticationProvider);  
  
        // 登录前验证token  
        httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);  
        return httpSecurity.build();  
    }  
  
    @Bean  
    WebSecurityCustomizer webSecurityCustomizer() {  
        return web -> web.ignoring().requestMatchers(  
                "/webjars/**",  
                "/api/swagger-ui.html/**",  
                "/swagger-resources/**",  
                "/api/swagger-resources/**",  
                "/v2/**",  
                "/v3/**",  
                "/swagger-ui/**",  
                "/swagger/**"  
        );  
    }  
  
    /**  
     * 配置 CORS  
     * @return    // 因为上边配置一直 有问题 所以这里也注掉了
     */  
 /*   @Bean  
    public CorsConfigurationSource corsConfigurationSource() {        CorsConfiguration configuration = new CorsConfiguration();        configuration.setAllowedOrigins(List.of("/**"));  // 允许所有来源  
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));  // 允许的方法  
        configuration.setAllowedHeaders(List.of("*"));  // 允许的头  
        configuration.setAllowCredentials(true);  // 允许凭证  
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();        source.registerCorsConfiguration("/**", configuration);        return source;    }*/    /**  
     * 装载BCrypt密码编码器  
     * @return  
     */  
    @Bean  
    public PasswordEncoder passwordEncoder() {  
        return new PasswordEncoder() {  
            @Override  
            public boolean matches(CharSequence rawPassword, String encodedPassword) {  
                return encodedPassword.equals(MD5Utils.getMD5((String) rawPassword));  
            }  
  
            @Override  
            public String encode(CharSequence rawPassword) {  
                return MD5Utils.getMD5((String) rawPassword);  
            }  
        };  
    }  
}

创建未授权处理逻辑文件 JwtAuthenticationEntryPoint

@Component  
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {  
  
    private static final long serialVersionUID = -8970718410437077606L;  
  
  
    public void commence(HttpServletRequest request,  
                         HttpServletResponse response,  
                         AuthenticationException authException) throws IOException {  
        //验证为未登陆状态会进入此方法,认证错误  
        //System.out.println("认证失败:" + authException.getMessage());  
        response.setStatus(200);  
        response.setCharacterEncoding("UTF-8");  
        response.setContentType("application/json; charset=utf-8");  
        PrintWriter printWriter = response.getWriter();  
     /*   JsonResultInfo<String> result=new JsonResultInfo<String>();  
        result.setCode("401");        result.setMsg(authException.getMessage());*/        ObjectMapper mapper =new ObjectMapper();  
        Map<String,Object> map =new HashMap<String, Object>();  
        map.put("code", "401");  
        map.put("msg", "用户没有登录,请登录");  
        //String body = ResultJson.failure(ResultCode.UNAUTHORIZED, authException.getMessage()).toString();  
        printWriter.write(mapper.writeValueAsString(map));  
       // printWriter.write(Gson.toJSONString(result));  
        printWriter.flush();  
    }  
}

创建权限不足文件 RestAuthenticationAccessDeniedHandler

@Component("restAuthenticationAccessDeniedHandler")  
public class RestAuthenticationAccessDeniedHandler implements AccessDeniedHandler {  
    @Override  
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {  
        // 登陆状态下,权限不足执行该方法  
        // System.out.println("权限不足:" + e.getMessage());  
        response.setStatus(HttpServletResponse.SC_FORBIDDEN); // 使用标准的403状态码  
        response.setCharacterEncoding("UTF-8");  
        response.setContentType("application/json; charset=utf-8");  
        PrintWriter printWriter = response.getWriter();  
  
        ObjectMapper mapper = new ObjectMapper();  
        Map<String, Object> map = new HashMap<>();  
        map.put("code", "403");  
        map.put("msg", "没有操作权限");  
        printWriter.write(mapper.writeValueAsString(map));  
        printWriter.flush();  
    }  
}

创建全局访问统一入口文件JwtAuthenticationTokenFilter

@Component  
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {  
  
      
  
    @Resource  
    private RedisTemplate<String, String> redisTemplate;  
  
    @Override  
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {  
       
        String token = request.getHeader("Authorization");  
        System.out.println(request.getRequestURL());  
       if(JwtUtils.vaild(token, Constants.LOGIN_SERCET) && SecurityContextHolder.getContext().getAuthentication() == null) {  
           Claims claims = JwtUtils.getClaims(token, Constants.LOGIN_SERCET);  
         
  
           String userId = (String)claims.get("userId");  
       
           //去redis 拿到token 进行对比  
           String token1 = (String) redisTemplate.opsForHash().get(Constants.TOKEN + ":" + userId+":", Constants.KEY);   //这里的常量可以自定义  与后边的文件对应上即可
     
           String userStr = (String) redisTemplate.opsForHash().get(Constants.TOKEN + ":" + userId+":", Constants.USER  );  
        
           if(token.equals(token1) &&  null !=userStr) {  
    
               ObjectMapper mapper =new ObjectMapper();  
               UserDetail userDetail = mapper.readValue(userStr, UserDetail.class);  
               redisTemplate.expire(Constants.TOKEN+":"+userId+":", 30, TimeUnit.MINUTES);  
               
            //   UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetail, null, userDetail.getAuthorities());               UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetail, null, userDetail.getAuthorities());  
               authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));  
               logger.info(String.format("Authenticated userDetail %s, setting security context", userDetail.getUsername()));  
               SecurityContextHolder.getContext().setAuthentication(authentication);  
           }  
             
       }  
          
 
        chain.doFilter(request, response);  
    }  
}

创建UserNameAuthenticationProvider类

@Slf4j  
@Component  
public class UserNameAuthenticationProvider implements AuthenticationProvider {  
  
    @Autowired  
    private LoginServiceImpl loginService;  
  
  
    @SneakyThrows  
    @Override    public Authentication authenticate(Authentication authentication) throws AuthenticationException {  
        long time = System.currentTimeMillis();  
        log.info("用户名/密码 开始登录验证 time:{}", time);  
        LoginDto params = (LoginDto) authentication.getPrincipal();  
        UserDetail userDetails =loginService.loadUserByUsername(params);  
        UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());  
        result.setDetails(authentication.getDetails());  
        log.info("用户名/密码 登录验证完成 time:{}, existTime:{}", time, (System.currentTimeMillis() - time));  
        return  result;  
  
  
  
  
    }  
  
    @Override  
    public boolean supports(Class<?> authentication) {  
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);  
    }  
  
  
  
}

创建 controller

@RestController  
@RequestMapping("/login")  
@Tag(name = "登录模块",description = "login")  
public class LoginController {  
  
    @Autowired  
    private AuthenticationManager authenticationManager;  
  
    @Resource  
    private RedisTemplate<String,String> redisTemplate;  
  
    @Operation(summary = "登录")  
    @PostMapping("/token")  
    public JsonResultInfo postToken(@RequestBody LoginDto param) throws JsonProcessingException {  
        Authentication authentication = null;  
        UserDetail userDetail = null;  
        authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(param, param.getPassWord()));  
        SecurityContextHolder.getContext().setAuthentication(authentication);  
        userDetail = (UserDetail) authentication.getPrincipal();  
  
        JsonResultInfo jsonResultInfo = new JsonResultInfo();  
        if (userDetail != null) {   // 用户不为空生成token 并存储  
            Claims cliams = Jwts.claims();  
            cliams.put("userId", userDetail.getId());  
            cliams.put("time", System.currentTimeMillis());  
            ObjectMapper mapper = new ObjectMapper();  
            Map<String, Object> map = new HashMap<String, Object>();  
            String token = JwtUtils.createToken(cliams, Constants.LOGIN_SERCET);  
            map.put("token", token);  
            map.put("userId", userDetail.getId());  
            //    map.put("clientCode", client.getClientCode());  
            //    redisTemplate.opsForValue().set(Constants.TOKEN+":"+userDetail.getId()+":"+type, mapper.writeValueAsString(userDetail), 1000, TimeUnit.MINUTES);            redisTemplate.opsForHash().put(Constants.TOKEN + ":" + userDetail.getId() + ":" , Constants.USER, mapper.writeValueAsString(userDetail));  
            redisTemplate.opsForHash().put(Constants.TOKEN + ":" + userDetail.getId() + ":" , Constants.KEY, token);  
            redisTemplate.expire(Constants.TOKEN + ":" + userDetail.getId() + ":", 30, TimeUnit.MINUTES);  
            jsonResultInfo.setCode(CommonEnum.LOGIN_SUCCESS.getResultCode());  
            jsonResultInfo.setMsg(CommonEnum.LOGIN_SUCCESS.getResultMsg());  
            jsonResultInfo.setData(map);  
            return jsonResultInfo;  
  
        }  
        return jsonResultInfo;  
    }  
  
}

整合完毕
访问http://localhost:yourPort/api/swagger-ui/index.html#/
访问 swagger

如果 swagger 配置错误会出现一些pet user 这些接口检索自己的 swagger 配置。

标签:return,springboot,spring,jwt,token,new,swagger,userDetail,public
From: https://www.cnblogs.com/al88cn/p/18518377

相关文章

  • 基于Springboot在线旅游景点网站【附源码+文档】
    ......
  • 揭秘!SpringBoot与MongoDB连接池配置,高效数据库操作的秘密武器
    在SpringBoot中整合MongoDB并配置连接池,可以通过以下步骤实现。我们将使用SpringDataMongoDB来简化与MongoDB的交互,并配置连接池以提高性能和资源利用率。一、添加依赖首先,在你的pom.xml文件中添加必要的依赖:<dependencies><!--SpringBootStarterDataMongoDB-......
  • SpringBoot调用外部接口的几种方式
    使用FeignClient调用FeignClient调用大多用于微服务开发中,各服务之间的接口调用。它以Java接口注解的方式调用HTTP请求,使服务间的调用变得简单1、在使用方引入依赖<!--Feign注解这里openFeign的版本要和自己使用的SpringBoot匹配--><dependency><groupId>org.springfr......
  • 基于SpringBoot+MySQL+SSM+Vue.js的交友系统(附论文)
    获取见最下方名片信息获取见最下方名片信息获取见最下方名片信息演示视频基于SpringBoot+MySQL+SSM+Vue.js的交友系统(附论文)技术描述开发工具:Idea/Eclipse数据库:MySQLJar包仓库:Maven前端框架:Vue/ElementUI后端框架:Spring+SpringMVC+Mybatis+SpringBoo......
  • 基于SpringBoot+MySQL+SSM+Vue.js的宠物猫售卖管理
    获取见最下方名片获取见最下方名片获取见最下方名片演示视频基于SpringBoot+MySQL+SSM+Vue.js的宠物猫售卖管理技术描述开发工具:Idea/Eclipse数据库:MySQLJar包仓库:Maven前端框架:Vue/ElementUI后端框架:Spring+SpringMVC+Mybatis+SpringBoot文字描述基......
  • 基于SpringBoot的旅游网站系统
    引言  在当今旅游业的数字化发展背景下,设计一个高效的旅游网站系统显得尤为重要。传统旅游网站往往存在页面加载缓慢、信息更新不及时、交互性差等问题,无法满足现代用户的需求。而基于SpringBoot+Vue+MySQL+MyBatis实现的旅游网站系统,通过前后端分离的架构设计,提升......
  • 音乐网站新篇章:SpringBoot Web实现
    2相关技术2.1MYSQL数据库MySQL是一个真正的多用户、多线程SQL数据库服务器。是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常适用于Web站点或者其他......
  • SpringBoot节奏:Web音乐网站构建手册
    2相关技术2.1MYSQL数据库MySQL是一个真正的多用户、多线程SQL数据库服务器。是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常适用于Web站点或者其他......
  • 基于SpringBoot的校园失物招领系统
    引言  在信息化快速发展的当下,校园内的失物招领管理是一个常见问题。传统的失物招领方式主要依靠公告栏或线下查询,存在信息滞后、查询不便等缺点,难以满足师生的实际需求。基于此,设计一款基于SpringBoot+Vue+MySQL+MyBatis的校园失物招领系统,采用前后端分离的架构,通......
  • 基于SpringBoot的智能餐厅点餐管理系统
    引言  随着数字化技术的发展,智慧餐厅逐渐成为餐饮行业提升服务质量和运营效率的重要方向。传统的点餐方式在高峰期经常导致顾客等待时间过长,影响用餐体验。智慧餐厅通过技术手段,实现了无纸化、自助式点餐,并有效减少人工操作,提高了点餐效率。本文介绍的基于SpringBoot+V......