>  기사  >  Java  >  SpringBoot+SpringSecurity+jwt를 사용하여 검증을 구현하는 방법

SpringBoot+SpringSecurity+jwt를 사용하여 검증을 구현하는 방법

WBOY
WBOY앞으로
2023-05-24 20:07:131588검색

Environment

  • springBoot 2.3.3

  • springSecurity 5.0

  • jjwt 0.91

pox.xml 파일 주요 정보

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

디렉터리 구조 정보

파일명 무시해주세요

SpringBoot+SpringSecurity+jwt를 사용하여 검증을 구현하는 방법

jwtAccessDeniedHandler 및 JwtAuthenticationEntryPoint

이 두 클래스의 기능은 사용자가 승인되지 않은 리소스에 액세스하고 오류 토큰을 운반할 수 있는 오류 반환 처리 정보 클래스입니다. 이 두 클래스를 사용하려면 보안 구성 파일에서 구성하기만 하면 됩니다.

/**
 * @author Bxsheng
 * @blogAddress www.kdream.cn
 * @createTIme 2020/9/17
 * since JDK 1.8
 * 当用户在没有授权的时候,返回的指定信息
 */
@Component
public class jwtAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        System.out.println("用户访问没有授权资源");
        System.out.println(e.getMessage());
        httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e==null?"用户访问没有授权资源":e.getMessage());

    }
}
/**
 * @author Bxsheng
 * @blogAddress www.kdream.cn
 * @createTIme 2020/9/17
 * since JDK 1.8
 */
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        System.out.println("用户访问资源没有携带正确的token");
        System.out.println(e.getMessage());
        httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e==null?"用户访问资源没有携带正确的token":e.getMessage());

    }
}
UserDetailsServiceImpl 로그인 정보 확인

이 클래스는 로그인 정보 확인을 위해 UserDetailsService를 직접 상속받습니다. 로그인을 위해 계정 비밀번호를 입력할 때 이 클래스를 입력하여 정보를 확인하게 됩니다.

물론 여기서는 직접 하드코딩된 비밀번호를 사용했습니다. 일반적으로 사용자 정보와 권한 정보는 데이터베이스에서 얻어야 합니다

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

        //直接写死数据信息,可以在这里获取数据库的信息并进行验证

        UserDetails userDetails  = User.withUsername(s).password(new BCryptPasswordEncoder().encode("123456"))
                .authorities("bxsheng").build();
        return userDetails;
    }
}
JwtTokenUtils jwt 패키징 클래스

이 클래스는 slyh의 [SpringBoot+JWT를 직접 사용합니다. 로그인 권한 제어(코드))]((https://www.yisu.com/article/257119.htm) 기사에 나오는 클래스입니다.

package cn.kdream.securityjwt.utlis;

import io.jsonwebtoken.*;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Bxsheng
 * @blogAddress www.kdream.cn
 * @createTIme 2020/9/16
 * since JDK 1.8
 */
public class JwtTokenUtils {
    public static final String TOKEN_HEADER = "Authorization";
    public static final String TOKEN_PREFIX = "Bearer ";
    public static final String SECRET = "jwtsecret";
    public static final String ISS = "echisan";

    private static final Long EXPIRATION = 60 * 60 * 3L; //过期时间3小时

    private static final String ROLE = "role";

    //创建token
    public static String createToken(String username, String role, boolean isRememberMe){
        Map map = new HashMap();
        map.put(ROLE, role);
        return Jwts.builder()
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .setClaims(map)
                .setIssuer(ISS)
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION * 1000))
                .compact();
    }

    //从token中获取用户名(此处的token是指去掉前缀之后的)
    public static String getUserName(String token){
        String username;
        try {
            username = getTokenBody(token).getSubject();
        } catch (    Exception e){
            username = null;
        }
        return username;
    }

    public static String getUserRole(String token){
        return (String) getTokenBody(token).get(ROLE);
    }

    private static Claims getTokenBody(String token){
        Claims claims = null;
        try{
            claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
        } catch(ExpiredJwtException e){
            e.printStackTrace();
        } catch(UnsupportedJwtException e){
            e.printStackTrace();
        } catch(MalformedJwtException e){
            e.printStackTrace();
        } catch(SignatureException e){
            e.printStackTrace();
        } catch(IllegalArgumentException e){
            e.printStackTrace();
        }
        return claims;
    }

    //是否已过期
    public static boolean isExpiration(String token){
        try{
            return getTokenBody(token).getExpiration().before(new Date());
        } catch(Exception e){
            System.out.println(e.getMessage());
        }
        return true;
    }
}
JwtAuthenticationFilter 사용자 정의 검증 jwt

이 클래스는 slyh의 [ SpringBoot +JWT는 로그인 권한 제어(코드)를 구현합니다.]((https://www.yisu.com/article/257119.htm)) 기사의 클래스입니다.

이 클래스의 주요 기능은 JWT 정보를 확인하는 것입니다. 주로 토큰 요청을 전달하고 JWT를 구문 분석하여 보안 컨텍스트에 설정합니다. 토큰에 포함된 권한 정보를 컨텍스트에 저장하는 것이 이 접근 방식의 목적 중 하나입니다. 사용자를 인증할 수 있습니다

/**
 * @author Bxsheng
 * @blogAddress www.kdream.cn
 * @createTIme 2020/9/16
 * since JDK 1.8
 */
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String tokenHeader = request.getHeader(JwtTokenUtils.TOKEN_HEADER);
        //如果请求头中没有Authorization信息则直接放行了
        if(tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtils.TOKEN_PREFIX)){
            chain.doFilter(request, response);
            return;
        }
        //如果请求头中有token,则进行解析,并且设置认证信息
        if(!JwtTokenUtils.isExpiration(tokenHeader.replace(JwtTokenUtils.TOKEN_PREFIX,""))){
            //设置上下文
            UsernamePasswordAuthenticationToken authentication = getAuthentication(tokenHeader);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        super.doFilterInternal(request, response, chain);
    }

    //获取用户信息
    private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader){
        String token = tokenHeader.replace(JwtTokenUtils.TOKEN_PREFIX, "");
        String username = JwtTokenUtils.getUserName(token);
        // 获得权限 添加到权限上去
        String role = JwtTokenUtils.getUserRole(token);
        List<GrantedAuthority> roles = new ArrayList<GrantedAuthority>();
        roles.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                return role;
            }
        });
        if(username != null){
            return new UsernamePasswordAuthenticationToken(username, null,roles);
        }
        return null;
    }

}
보안 구성 정보

@EnableGlobalMethodSecurity(prePostEnabled = true) prePostEnabled 주석 인증을 켜세요

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityJwtConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    private jwtAccessDeniedHandler jwtAccessDeniedHandler;

    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
       http.cors().and().csrf().disable().authorizeRequests()
               .antMatchers(HttpMethod.OPTIONS,"/**")
               .permitAll()
               .antMatchers("/").permitAll()
               //login 不拦截
               .antMatchers("/login").permitAll()
               .anyRequest().authenticated()
               //授权
               .and()
               // 禁用session
               .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
       // 使用自己定义的拦截机制,拦截jwt
        http.addFilterBefore(new JwtAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class)
        //授权错误信息处理
                .exceptionHandling()
                //用户访问资源没有携带正确的token
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                //用户访问没有授权资源
                .accessDeniedHandler(jwtAccessDeniedHandler);
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        //使用的密码比较方式
        return  new BCryptPasswordEncoder();
    }

}
Startup 클래스

Startup 클래스에 세 가지를 구성했습니다. Methods, 하나는 로그인에 사용됩니다. 정보, 나머지 2개는 권한 접근을 요구하도록 설정되어 있습니다

@SpringBootApplication
@RestController
public class SecurityJwtApplication {

    private final AuthenticationManagerBuilder authenticationManagerBuilder;

    public SecurityJwtApplication(AuthenticationManagerBuilder authenticationManagerBuilder) {
        this.authenticationManagerBuilder = authenticationManagerBuilder;
    }

    public static void main(String[] args) {
        SpringApplication.run(SecurityJwtApplication.class, args);
    }



    @GetMapping("/")
    public String index(){
        return "security jwt";
    }

    @PostMapping("/login")
    public String login(@RequestParam String u,@RequestParam String p){
        // 登陆验证
        UsernamePasswordAuthenticationToken token =
                new UsernamePasswordAuthenticationToken(u, p);
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(token);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        //创建jwt信息
        String token1 = JwtTokenUtils.createToken(u,"bxsheng", true);
        return token1;
    }

    @GetMapping("/role")
    @PreAuthorize("hasAnyAuthority(&#39;bxsheng&#39;)")
    public String roleInfo(){
        return "需要获得bxsheng权限,才可以访问";
    }

    @GetMapping("/roles")
    @PreAuthorize("hasAnyAuthority(&#39;kdream&#39;)")
    public String rolekdream(){
        return "需要获得kdream权限,才可以访问";
    }
}
Effect

인증이 필요한 사용자 정보에 직접 접근

토큰을 사용하지 않고 인증만 필요한 리소스 정보에 직접 접근하면 JwtAuthenticationEntryPoint 클래스에 들어갑니다

SpringBoot+SpringSecurity+jwt를 사용하여 검증을 구현하는 방법

Get token

토큰 정보를 얻기 위해 스타트업 클래스에서 로그인 메소드에 접근

고정 비밀번호를 사용하기 때문에, 잘못된 비밀번호로 접속시 글로벌 springboot에서 접속이 가능함 예외정보가 캡쳐됨 예외 처리 중

/**
 * @author Bxsheng
 * @blogAddress www.kdream.cn
 * @createTIme 2020/9/17
 * since JDK 1.8
 */
@RestControllerAdvice
public class Error {
    @ExceptionHandler(BadCredentialsException.class)
    public void badCredentialsException(BadCredentialsException e){
        System.out.println(e.getMessage());//用户名或密码错误

       // throw new  BadCredentialsException(e.getMessage());
    }
}

SpringBoot+SpringSecurity+jwt를 사용하여 검증을 구현하는 방법

토큰을 올바르게 획득하고 보호된 리소스 액세스 수행

하드코딩된 bxsheng 권한 정보가 있어 bxsheng에서 식별된 리소스 정보를 정상적으로 얻을 수 있습니다.

SpringBoot+SpringSecurity+jwt를 사용하여 검증을 구현하는 방법

정보 획득 성공

SpringBoot+SpringSecurity+jwt를 사용하여 검증을 구현하는 방법

승인되지 않은 리소스 정보 획득 시도

토큰을 사용하여 승인되지 않은 리소스 정보에 직접 접근하면 jwtAccessDeniedHandler 클래스에 진입하게 됩니다

SpringBoot+SpringSecurity+jwt를 사용하여 검증을 구현하는 방법

위 내용은 SpringBoot+SpringSecurity+jwt를 사용하여 검증을 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제