pom包导入
<!--spring security --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!-- java-jwt --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>
其余依赖包按需加入。
SecurityConfig配置
由于spring boot3.0废弃了extends WebSecurityConfigurerAdapter 的方式,所以这里采用添加@Bean新方式
packagecom.mia.common.config;importcom.mia.common.filter.JwtRequestFilter;importlombok.extern.slf4j.Slf4j;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.http.HttpMethod;importorg.springframework.security.authentication.AuthenticationManager;importorg.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.EnableWebSecurity;importorg.springframework.security.config.http.SessionCreationPolicy;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.web.SecurityFilterChain;importorg.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;importorg.springframework.web.cors.CorsConfiguration;importorg.springframework.web.cors.CorsConfigurationSource;importorg.springframework.web.cors.UrlBasedCorsConfigurationSource;importjava.util.List;@Slf4j@Configuration@EnableWebSecuritypublicclassSecurityConfig{@AutowiredprivateAuthenticationConfiguration authenticationConfiguration;@BeanpublicJwtRequestFilterauthenticationJwtTokenFilter(){returnnewJwtRequestFilter();}/**
* 加密方式
*/@BeanpublicBCryptPasswordEncoderbCryptPasswordEncoder(){returnnewBCryptPasswordEncoder();}/**
* 认证管理器,登录的时候参数会传给 authenticationManager
*
*/@BeanpublicAuthenticationManagerauthenticationManager(AuthenticationConfiguration authenticationConfiguration)throwsException{return authenticationConfiguration.getAuthenticationManager();}@BeanpublicSecurityFilterChainsecurityFilterChain(HttpSecurity http)throwsException{//关闭csrf
http.csrf().disable()// 允许跨域(也可以不允许,看具体需求).cors().and()//不通过Session获取SecurityContext.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()// 配置路径是否需要认证.authorizeRequests()// 对于登录接口 允许匿名访问.antMatchers(HttpMethod.POST,"/account/**").permitAll()// 配置权限.antMatchers("/test").hasAuthority("admin")// 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated().and().authenticationManager(authenticationManager(authenticationConfiguration)).sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()//此处为添加jwt过滤.addFilterBefore(authenticationJwtTokenFilter(),UsernamePasswordAuthenticationFilter.class);
http.headers().frameOptions().disable();return http.build();}/**
*跨域资源配置
*/@BeanpublicCorsConfigurationSourcecorsConfigurationSource(){finalCorsConfiguration configuration =newCorsConfiguration();//此处发现如果不加入自己的项目地址,会被拦截。
configuration.setAllowedOriginPatterns(List.of("http://localhost:8083"));
configuration.setAllowedMethods(List.of("GET","POST","OPTIONS","DELETE","PUT","PATCH"));
configuration.setAllowedHeaders(List.of("Access-Control-Allow-Origin","X-Requested-With","Origin","Content-Type","Accept","Authorization"));
configuration.setAllowCredentials(true);finalUrlBasedCorsConfigurationSource source =newUrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);return source;}}
JWT过滤
此处为以上过滤链中前置过滤中配置的过滤器,并且也是jwt与security主要集成使用的过滤类
packagecom.mia.common.filter;importcom.mia.common.utils.JwtTokenUtil;importcom.mia.loginProjet.account.UserDetailsServiceImpl;importjakarta.servlet.FilterChain;importjakarta.servlet.ServletException;importjakarta.servlet.http.HttpServletRequest;importjakarta.servlet.http.HttpServletResponse;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.security.authentication.UsernamePasswordAuthenticationToken;importorg.springframework.security.core.context.SecurityContextHolder;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.security.web.authentication.WebAuthenticationDetailsSource;importorg.springframework.util.StringUtils;importorg.springframework.web.filter.OncePerRequestFilter;importjava.io.IOException;publicclassJwtRequestFilterextendsOncePerRequestFilter{privatestaticfinalLogger log =LoggerFactory.getLogger(JwtRequestFilter.class);publicstaticfinalString BEARER ="Bearer ";@AutowiredprivateJwtTokenUtil jwtTokenUtil;@AutowiredprivateUserDetailsServiceImpl userDetailsService;/**
* 从 Authorization 标头中,提取令牌
*
*/privateStringparseJwt(HttpServletRequest request){String headerAuth = request.getHeader("Authorization");if(StringUtils.hasText(headerAuth)&& headerAuth.startsWith(BEARER)){return headerAuth.substring(BEARER.length());}returnnull;}@OverrideprotectedvoiddoFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain)throwsServletException,IOException{try{String jwt =parseJwt(request);if(jwt !=null&& jwtTokenUtil.validateJwtToken(jwt)){String username = jwtTokenUtil.getUsernameFromJwtToken(jwt);// 如果令牌存在(存在逻辑自己编写),则加载令牌UserDetails userDetails = userDetailsService.loadUserByUsername(username);UsernamePasswordAuthenticationToken authenticationToken =newUsernamePasswordAuthenticationToken(
userDetails,null, userDetails.getAuthorities());
authenticationToken.setDetails(newWebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(authenticationToken);}}catch(Exception e){
log.error("无法设置用户认证:{}", e);}
filterChain.doFilter(request, response);}}
JwtTokenUtil
此处为jwt操作封装了一些工具类,为生成token和解析token数据所用
packagecom.mia.common.utils;importio.jsonwebtoken.*;importlombok.extern.slf4j.Slf4j;importorg.springframework.context.annotation.Configuration;importorg.springframework.stereotype.Component;importjava.util.Date;importjava.util.HashMap;importjava.util.Map;@Configuration@Component@Slf4jpublicclassJwtTokenUtil{/**
* token的头key
*/publicstaticfinalString TOKEN_HEADER ="Authorization";/**
* token前缀
*/publicstaticfinalString TOKEN_PREFIX ="Bearer ";/**
* token 过期时间 30分钟
*/publicstaticfinallong EXPIRATION =1000*60*30;/**
* 加密的key
*/publicstaticfinalString APP_SECRET_KEY ="secret";/**
* 权限的声明key
*/privatestaticfinalString ROLE_CLAIMS ="role";/**
* 生成token
*
* @param username 用户名
* @param role 用户角色
* @return token
*/publicstaticStringcreateToken(String username,String role){Map<String,Object> map =newHashMap<>();
map.put(ROLE_CLAIMS, role);String token =Jwts.builder().setSubject(username)//.setClaims(map)// .claim("username", username).setIssuedAt(newDate()).setExpiration(newDate(System.currentTimeMillis()+ EXPIRATION)).signWith(SignatureAlgorithm.HS256, APP_SECRET_KEY).compact();return TOKEN_PREFIX +token;}/**
* 获取当前登录用户用户名
*
* @param token
* @return
*/publicstaticStringgetUsername(String token){Claims claims =Jwts.parser().setSigningKey(APP_SECRET_KEY).parseClaimsJws(token).getBody();return claims.get("username").toString();}/**
* 获取当前登录用户角色
*
* @param token
* @return
*/publicstaticStringgetUserRole(String token){Claims claims =Jwts.parser().setSigningKey(APP_SECRET_KEY).parseClaimsJws(token).getBody();return claims.get("rol").toString();}/**
* 检查token是否过期
*
* @param token token
* @return boolean
*/publicstaticbooleanisExpiration(String token){Claims claims =Jwts.parser().setSigningKey(APP_SECRET_KEY).parseClaimsJws(token).getBody();return claims.getExpiration().before(newDate());}publicbooleanvalidateJwtToken(String authToken){try{Jwts.parser().setSigningKey(APP_SECRET_KEY).parseClaimsJws(authToken);returntrue;}catch(SignatureException e){
log.error("Invalid JWT signature: {}", e.getMessage());}catch(MalformedJwtException e){
log.error("Invalid JWT token: {}", e.getMessage());}catch(ExpiredJwtException e){
log.error("JWT token is expired: {}", e.getMessage());}catch(UnsupportedJwtException e){
log.error("JWT token is unsupported: {}", e.getMessage());}catch(IllegalArgumentException e){
log.error("JWT claims string is empty: {}", e.getMessage());}returnfalse;}publicStringgetUsernameFromJwtToken(String token){returnJwts.parser().setSigningKey(APP_SECRET_KEY).parseClaimsJws(token).getBody().getSubject();}}
UserDetailsServiceImpl
这里需要自己去实现Security提供的UserDetailsService接口
重写loadUserByUsername方法,这里是验证token中信息是否可以允许访问的逻辑
packagecom.mia.loginProjet.account;importorg.springframework.security.core.GrantedAuthority;importorg.springframework.security.core.userdetails.User;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.core.userdetails.UsernameNotFoundException;importorg.springframework.stereotype.Service;importjava.util.ArrayList;importjava.util.List;@ServicepublicclassUserDetailsServiceImplimplementsUserDetailsService{@OverridepublicUserDetailsloadUserByUsername(String username)throwsUsernameNotFoundException{GrantedAuthority grantedAuthority=()->"roladmin";List<GrantedAuthority> grantedAuthorities=newArrayList<>();
grantedAuthorities.add(grantedAuthority);//这里可以实现从数据库取,我为了偷懒,就没这么做 - -returnnewUser("admin","123",grantedAuthorities);}}
尝试自己实现一个登录接口
这里是我的例子,我偷懒写了一个登录接口,为了得到token,也为了以上过滤测试,可以实现成直接存在web头里,但我这里直接返回给前端处理,哈哈哈
packagecom.mia.loginProjet.account.controller;importcom.mia.common.dto.LoginPwdDto;importcom.mia.common.utils.JwtTokenUtil;importorg.springframework.web.bind.annotation.*;@RestControllerpublicclass accountController {@PostMapping("/account/login")publicStringlogin(@RequestBodyLoginPwdDto loginPwdDto){// 如果认证没通过,提示// 认证通过返回jwt//TODO 数据库验证用户账号逻辑 我懒没实现String jwt =JwtTokenUtil.createToken("admin","admin");System.out.println("JWT--------"+jwt);//放入请求头return jwt;}@GetMapping("/test")publicStringtest1(){return"200";}@GetMapping("/test2")publicStringtest2(){return"200";}}
至此结束示例,创作不易,可以收藏,但请不要抄袭
版权归原作者 mia_tong1111 所有, 如有侵权,请联系我们删除。