Maison >Java >javaDidacticiel >Comment utiliser SpringBoot+SpringSecurity+jwt pour implémenter la vérification

Comment utiliser SpringBoot+SpringSecurity+jwt pour implémenter la vérification

WBOY
WBOYavant
2023-05-24 20:07:131729parcourir

Environnement

  • springBoot 2.3.3

  • springSecurity 5.0

  • jjwt 0.91

informations principales sur le fichier 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>

Informations sur la structure du répertoire

Veuillez ignorer le nom du fichier

Comment utiliser SpringBoot+SpringSecurity+jwt pour implémenter la vérification

jwtAccessDeniedHandler et JwtAuthenticationEntryPoint

Les fonctions de ces deux classes sont des classes d'informations de traitement de retour d'erreur permettant aux utilisateurs d'accéder à des ressources non autorisées et de transporter des jetons d'erreur. Pour utiliser ces deux classes, il vous suffit de les configurer dans le fichier de configuration de sécurité

.
/**
 * @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 Vérification des informations de connexion

Cette classe hérite directement de UserDetailsService pour la vérification des informations de connexion Lorsque vous entrez le mot de passe du compte pour vous connecter, vous entrerez dans cette classe pour vérifier les informations.

Bien sûr, j'ai utilisé directement un mot de passe codé en dur ici. Normalement, les informations de l'utilisateur et les informations d'autorisation doivent être obtenues à partir de la base de données

@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;
    }
}

Classe d'emballage JwtTokenUtils jwt

Cette classe utilise directement [SpringBoot+JWT to] de Slyh. implémenter le contrôle des autorisations de connexion (code))]((https://www.yisu.com/article/257119.htm) La classe dans l'article.

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 Vérification personnalisée jwt

Cette classe utilise directement celui de slyh [ SpringBoot +JWT implémente le contrôle des autorisations de connexion (code)]((https://www.yisu.com/article/257119.htm)) La classe dans l'article.

La fonction principale de cette classe est de vérifier les informations jwt. Elle transporte principalement la demande de jeton, analyse le jwt et le définit dans le contexte de sécurité. L'enregistrement des informations d'autorisation contenues dans le jeton dans le contexte est l'un des objectifs de cette approche. Vous pouvez authentifier les utilisateurs

/**
 * @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;
    }

}

informations de configuration de sécurité

@EnableGlobalMethodSecurity(prePostEnabled = true) Activer l'autorisation d'annotation 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();
    }

}

Classe de démarrage

J'en ai configuré trois dans la classe de démarrage Méthodes, une est utilisée pour se connecter informations, et les deux autres sont configurés pour exiger un accès avec autorisation

@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

L'accès direct aux informations utilisateur qui nécessitent une autorisation

L'accès direct aux informations sur les ressources qui nécessitent uniquement une autorisation sans utiliser de jeton entrera dans la classe JwtAuthenticationEntryPoint

Comment utiliser SpringBoot+SpringSecurity+jwt pour implémenter la vérification

Obtenir un jeton

Accédez à la méthode de connexion dans la classe de démarrage pour obtenir des informations sur le jeton

Parce que j'utilise un mot de passe fixe, lorsque j'accède avec un mot de passe incorrect, je peux y accéder dans le springboot global. Les informations d'exception sont capturées pendant la gestion des exceptions

/**
 * @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());
    }
}

Comment utiliser SpringBoot+SpringSecurity+jwt pour implémenter la vérification

Obtenez correctement le jeton et effectuez un accès aux ressources protégées

Il contient des informations d'autorisation bxsheng codées en dur, de sorte que les informations sur les ressources identifiées par bxsheng peuvent être obtenues normalement. "Informations obtenues avec succès"

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer