SpringBoot-Spring-security之JWT授权认证
JWT是现在前后端分离用的比较多的验证授权方式
Maven引入
<!--安全框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--JSON封装-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.36</version>
</dependency>
<!--JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
核心思路
需要配置WebSecurityConfigurerAdapter
在configuration中的configure方法加入自定义认证
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 加入自定义的安全认证 auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder()); }
http的configure
// 去掉 CSRF http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 使用 JWT,关闭token .and() .httpBasic().authenticationEntryPoint(authenticationEntryPoint) .and() .authorizeRequests() .anyRequest() .access("@rbacauthorityservice.hasPermission(request,authentication)") // RBAC 动态 url 认证 .and() .formLogin() //开启登录 .successHandler(authenticationSuccessHandler) // 登录成功 .failureHandler(authenticationFailureHandler) // 登录失败 .permitAll() .and() .logout() .logoutSuccessHandler(logoutSuccessHandler) .permitAll(); // 记住我 http.rememberMe().rememberMeParameter("remember-me") .userDetailsService(userDetailsService).tokenValiditySeconds(300); http.exceptionHandling().accessDeniedHandler(accessDeniedHandler); // 无权访问 JSON 格式的数据 http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); // JWT Filter
需要编写一个JWT的Token生成Util
/** * JWT工具类,使用RSA加密方式 */ public class JwtTokenUtil { private static InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("jwt.jks"); // 寻找证书文件 private static PrivateKey privateKey = null; private static PublicKey publicKey = null; static { // 将证书文件里边的私钥公钥拿出来 try { KeyStore keyStore = KeyStore.getInstance("JKS"); // java key store 固定常量 keyStore.load(inputStream, "123456".toCharArray()); privateKey = (PrivateKey) keyStore.getKey("jwt", "123456".toCharArray()); // jwt 为 命令生成整数文件时的别名 publicKey = keyStore.getCertificate("jwt").getPublicKey(); } catch (Exception e) { e.printStackTrace(); } } public static String generateToken(String subject, int expirationSeconds, String salt) { return Jwts.builder() .setClaims(null) .setSubject(subject) .setExpiration(new Date(System.currentTimeMillis() + expirationSeconds * 1000)) // .signWith(SignatureAlgorithm.HS512, salt) // 不使用公钥私钥 .signWith(SignatureAlgorithm.RS256, privateKey) .compact(); } public static String parseToken(String token, String salt) { String subject = null; try { Claims claims = Jwts.parser() // .setSigningKey(salt) // 不使用公钥私钥 .setSigningKey(publicKey) .parseClaimsJws(token).getBody(); subject = claims.getSubject(); } catch (Exception e) { } return subject; } }
核心接口
** 自定义AccessDeniedHandler实现类 **
作用和说明:
用来解决认证过(认证过可能是认证成功,可能是认证失败,统一称为认证过)的用户访问无权限资源时的异常
Spring 默认的AccessDeniedHandler只有对页面的处理,而此处我们是Ajax请求
如果需要同时处理,可根据HTTPUtils.isAjaxRequest(request)来判断即可
@Override public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException { AjaxResponseBody responseBody = new AjaxResponseBody(); responseBody.setStatus("300"); responseBody.setMsg("Need Authorities!"); httpServletResponse.getWriter().write(JSON.toJSONString(responseBody)); }
** 自定义AuthenticationEntryPoint实现类**
作用和说明:用来解决匿名用户 访问无权限资源时的异常处理
@Override public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { AjaxResponseBody responseBody = new AjaxResponseBody(); responseBody.setStatus("000"); responseBody.setMsg("Need Authorities!"); httpServletResponse.getWriter().write(JSON.toJSONString(responseBody)); }
** 自定义AuthenticationFailureHandler实现类
作用和说明:自定义 认证失败 处理器
@Override public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { AjaxResponseBody responseBody = new AjaxResponseBody(); responseBody.setStatus("400"); responseBody.setMsg("Login Failure!"); httpServletResponse.getWriter().write(JSON.toJSONString(responseBody)); }
** 自定义AuthenticationSuccessHandler实现类 **
作用和说明:自定义认证成功处理器,并分配JWT的Token
@Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { AjaxResponseBody responseBody = new AjaxResponseBody(); responseBody.setStatus("200"); responseBody.setMsg("Login Success!"); SelfUserDetails userDetails = (SelfUserDetails) authentication.getPrincipal(); String jwtToken = JwtTokenUtil.generateToken(userDetails.getUsername(), 300, "_secret"); responseBody.setJwtToken(jwtToken); httpServletResponse.getWriter().write(JSON.toJSONString(responseBody)); }
** 自定义LogoutSuccessHandler实现类 **
作用和说明:成功退出的处理器
@Override public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { AjaxResponseBody responseBody = new AjaxResponseBody(); responseBody.setStatus("100"); responseBody.setMsg("Logout Success!"); httpServletResponse.getWriter().write(JSON.toJSONString(responseBody)); }
需要些一个Filter来拦截header里面的Token
关键代码
String username = JwtTokenUtil.parseToken(authToken, "_secret"); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (userDetails != null) { UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } }
** RBAC服务**
/** * RBAC权限访问控制 */ @Component("rbacauthorityservice") public class RbacAuthorityService { public boolean hasPermission(HttpServletRequest request, Authentication authentication) { Object userInfo = authentication.getPrincipal(); boolean hasPermission = false; if (userInfo instanceof UserDetails) { String username = ((UserDetails) userInfo).getUsername(); //获取资源 Set<String> urls = new HashSet(); urls.add("/common/**"); // 这些 url 都是要登录后才能访问,且其他的 url 都不能访问! Set set2 = new HashSet(); Set set3 = new HashSet(); AntPathMatcher antPathMatcher = new AntPathMatcher(); for (String url : urls) { if (antPathMatcher.match(url, request.getRequestURI())) { hasPermission = true; break; } } return hasPermission; } else { return false; } } }
还没有评论,来说两句吧...