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>
##jwtAccessDeniedHandler および JwtAuthenticationEntryPointファイル名は無視してください
これら 2 つのクラスの役割は、ユーザーが未承認のリソースにアクセスし、エラー トークンを運ぶためのエラー戻り処理情報クラスを提供することです。これら 2 つのクラスを使用するには、セキュリティ構成ファイルで構成し、UserDetailsServiceImpl ログイン情報検証/** * @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()); } }
このクラスは、ログイン情報検証のために UserDetailsService を直接継承し、アカウントのパスワードを入力してください ログイン時に、情報を確認するためにこのクラスが入力されます。 もちろん、ここではハードコードされたパスワードを直接使用しています。通常、ユーザーの情報と権限情報はデータベースから取得する必要がありますJwtTokenUtils jwt パッケージング クラス@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; } }
このクラスは、slyh さんの記事「ログイン権限制御を実装する SpringBoot JWT (コード))]((https://www.yisu.com/article/257119.htm)」のクラスを直接利用しています。JwtAuthenticationFilter カスタム検証 jwtpackage 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; } }
このクラスは、slyh の [SpringBoot JWT を使用してログイン許可制御を実装します (コード))](( https://www.yisu.com /article /257119.htm))) 記事のクラス内。 このクラスの主な機能は、jwt 情報を検証することであり、主にトークン リクエストを送信し、jwt を解析し、セキュリティ コンテキストに設定します。トークンに含まれる許可情報をコンテキストに保存することが、このアプローチの目的の 1 つです。ユーザーを認証できますセキュリティ構成情報/** * @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 アノテーション承認を有効にするStartup class@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(); } }
スタートアップ クラスに 3 つのメソッドを構成しました。1 つはログイン情報に使用され、他の 2 つはアクセス許可を必要とするように設定されています。Effect認可が必要なユーザー情報への直接アクセス@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('bxsheng')") public String roleInfo(){ return "需要获得bxsheng权限,才可以访问"; } @GetMapping("/roles") @PreAuthorize("hasAnyAuthority('kdream')") public String rolekdream(){ return "需要获得kdream权限,才可以访问"; } }
トークンを使用せず認可のみが必要なリソース情報への直接アクセスは、JwtAuthenticationEntryPointクラスに入ります
Get token
スタートアップクラスのloginメソッドにアクセスし、トークン情報を取得します固定パスワードを使用しているため、間違ったパスワードでアクセスした場合は、 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()); } }
bxsheng の許可情報がコーディングされているため、通常は bxsheng で識別されるリソース情報を取得できます。
# 情報の取得に成功しました
#不正なリソース情報を取得しようとしました
#トークンを使用して未承認のリソース情報に直接アクセスすると、jwtAccessDeniedHandler クラスに入ります以上がSpringBoot+SpringSecurity+jwtを使用して検証を実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。