ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Security提高-集成JWT

2022-08-23 11:30:17  阅读:158  来源: 互联网

标签:集成 return String JWT springframework org import Security security


注意:请先看《springboot从0开始搭建rbac的security权限认证》再看本篇

1.引入jwt依赖

<!-- JWT -->
       <dependency>
           <groupId>io.jsonwebtoken</groupId>
           <artifactId>jjwt</artifactId>
           <version>0.9.1</version>
       </dependency>

2.在application.properties添加JWT的配置

# JWT??
jwt:
 # JWT??????
tokenHeader: Authorization
 # JWT ???????
secret: wrs-secret
 # JWT ?????(60*60*24)
expiration: 604800
 # JWT???????
tokenHead: Bearer
spring:
datasource:
  driver-class-name: com.mysql.cj.jdbc.Driver
  url: jdbc:mysql://localhost:3306/user_db?useSSL=false&serverTimezone=Hongkong&useUnicode=true&characterEncoding=utf8&autoReconnect=true
  username: root
  password: 123456
server:
port: 9999

3.书写JWT工具类

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

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

@Component
public class JwtTokenUtil {
   // 用户名的key
   private static final String CLAIM_KEY_USERNAME = "sub";
   // jwt创建时间
   private static final String CLAIM_KEY_CREATED = "created";

   /**
    * 去application.yml拿jwt密钥和jwt失效时间
    */
   @Value("${jwt.secret}")
   private String secret;
   @Value("${jwt.expiration}")
   private Long expiration;

   /**
    * 根据用户信息生成Token
    *
    * @param userDetails
    * @return
    */
   public String generateToken(UserDetails userDetails) {
       HashMap<String, Object> claims = new HashMap<>();
       claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
       claims.put(CLAIM_KEY_CREATED, new Date());
       return generateToken(claims);
  }

   /**
    * 从Token中获取username
    * @param token
    * @return
    */
   public String getUsernameFromToken(String token) {
       String username;
       try {
           Claims claims = getClaimsFromToken(token);
           username = claims.getSubject();
      } catch (Exception e) {
           username = null;
      }
       return username;
  }

   /**
    * 从Token中获取荷载
    * @param token
    * @return
    */
   private Claims getClaimsFromToken(String token) {
       Claims claims = null;
       try {
           claims = Jwts.parser()
                  .setSigningKey(secret)
                  .parseClaimsJws(token)
                  .getBody();
      } catch (Exception e) {
           e.printStackTrace();
      }
       return claims;
  }

   /**
    * 验证Token是否有效
    * @param token
    * @param userDetails
    * @return
    */
   public boolean validateToken(String token, UserDetails userDetails) {
       String username = getUsernameFromToken(token);
       return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
  }

   /**
    * 判断Token是否失效
    * @param token
    * @return
    */
   private boolean isTokenExpired(String token) {
       Date expireDate = getExpiredDateFromToken(token);
       return expireDate.before(new Date());
  }

   /**
    * 从Token中获取过期时间
    * @param token
    * @return
    */
   private Date getExpiredDateFromToken(String token) {
       Claims claims = getClaimsFromToken(token);
       return claims.getExpiration();
  }

   /**
    * 根据荷载生成JWT Token
    *
    * @param claims
    * @return
    */
   private String generateToken(Map<String, Object> claims) {
       return Jwts.builder()
              .setClaims(claims)
              .setExpiration(generateExpirationDate())
              .signWith(SignatureAlgorithm.HS512, secret)
              .compact();
  }

   /**
    * 生成Token失效时间
    *
    * @return
    */
   private Date generateExpirationDate() {
       return new Date(System.currentTimeMillis() + expiration * 1000);
  }
}

4.书写JWT的过滤器

import com.zhuoyue.util.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {

   @Value("${jwt.tokenHeader}")
   private String tokenHeader;
   @Value("${jwt.tokenHead}")
   private String tokenHead;
   @Resource
   private JwtTokenUtil jwtTokenUtil;
   @Resource
   private UserDetailsService userDetailsService;
   @Override
   protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
           throws ServletException, IOException, NumberFormatException {
       // 获取Header
       String authHeader = request.getHeader(tokenHeader);
       // 存在token但不是tokenHead开头
       if (null != authHeader && authHeader.startsWith(tokenHead)) {
           // 字段截取authToken
           String authToken = authHeader.substring(tokenHead.length());
           // 根据authToken获取username
           String username = jwtTokenUtil.getUsernameFromToken(authToken);
           // token存在用户名但未登录
           if (null != username && null == SecurityContextHolder.getContext().getAuthentication()) {
               // 登录
               UserDetails userDetails = userDetailsService.loadUserByUsername(username);
               // 验证token是否有效,如果有效,将他重新放到用户对象里。
               if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                   UsernamePasswordAuthenticationToken authenticationToken =
                           new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                   // 重新设置到用户对象里
                   authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                   SecurityContextHolder.getContext().setAuthentication(authenticationToken);
              }
          }
      }
       // 放行
       chain.doFilter(request, response);
  }
}

5.书写未登录拦截

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhuoyue.common.RespBean;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Component
public class RestAuthorizationEntryPoint implements AuthenticationEntryPoint {
   @Override
   public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
           throws IOException, ServletException {
       // 通过response设置编码格式
       response.setCharacterEncoding("UTF-8");
       // 设置ContentType
       response.setContentType("application/json");
       // 输出流
       PrintWriter out = response.getWriter();
       RespBean bean = RespBean.error("未登录,请登录!");
       bean.setCode(401);
       out.write(new ObjectMapper().writeValueAsString(bean));
       out.flush();
       out.close();
  }
}

6.书写权限不足拦截

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhuoyue.common.RespBean;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;


@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {
   @Override
   public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
           throws IOException, ServletException {
       // 通过response设置编码格式
       response.setCharacterEncoding("UTF-8");
       // 设置ContentType
       response.setContentType("application/json");
       // 输出流
       RespBean bean = RespBean.error("权限不足,请联系管理员!");
       PrintWriter out = response.getWriter();
       bean.setCode(403);
       out.write(new ObjectMapper().writeValueAsString(bean));
       out.flush();
       out.close();
  }
}

7.修改UserService添加login接口

import com.zhuoyue.common.RespBean;
import org.springframework.security.core.userdetails.UserDetailsService;

public interface UserService extends UserDetailsService {

   RespBean login(String username,String password);
}

8.书写Uservice的实现类UserServiceImpl

import com.zhuoyue.common.RespBean;
import com.zhuoyue.mapper.PerssionMapper;
import com.zhuoyue.mapper.UserMapper;
import com.zhuoyue.po.PerssionPO;
import com.zhuoyue.po.UserPO;
import com.zhuoyue.servcie.UserService;
import com.zhuoyue.util.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl implements UserService {
   @Resource
   private PasswordEncoder passwordEncoder;
   @Value("${jwt.tokenHead}")
   private String tokenHead;
   @Resource
   private UserMapper userMapper;
   @Resource
   private JwtTokenUtil jwtTokenUtil;

   @Override
   public RespBean login(String username, String password) {
       UserDetails userDetails = loadUserByUsername(username);
       if (userDetails == null)return RespBean.error("登录失败!");
       if (passwordEncoder.matches(password,userDetails.getPassword())){
           if (userDetails.isEnabled()){
               UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
               SecurityContextHolder.getContext().setAuthentication(authenticationToken);
               String token = jwtTokenUtil.generateToken(userDetails);
               HashMap<Object, Object> map = new HashMap<>();
               map.put("token",token);
               map.put("tokenHead",tokenHead);
               return RespBean.success("登录成功!",map);
          }
           return RespBean.error("账号已被禁用请联系管理员");
      }
       return RespBean.error("用户名或密码错误!");
  }

   @Override
   public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
       UserPO userPO = userMapper.findUserByUserName(s);
       if (userPO==null)return null;
       List<PerssionPO> all = userMapper.findPerssionByUserName(s);
       List<GrantedAuthority> collect = all.stream().map(perssionPO -> new SimpleGrantedAuthority(perssionPO.getCode())).collect(Collectors.toList());
       userPO.setAuthorities(collect);
       return userPO;
  }
}

9.修改security的配置类

import com.zhuoyue.mapper.PerssionMapper;
import com.zhuoyue.po.PerssionPO;
import com.zhuoyue.servcie.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;
import java.util.List;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
   @Resource
   private RestAuthorizationEntryPoint restAuthorizationEntryPoint;
   @Resource
   private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
   @Resource
   private UserService userService;
   @Resource
   private PerssionMapper perssionMapper;

   @Bean
   public PasswordEncoder passwordEncoder() {
       return new BCryptPasswordEncoder();
  }
   @Bean
   public JwtAuthorizationTokenFilter jwtAuthorizationTokenFilter() {
       return new JwtAuthorizationTokenFilter();
  }

   @Override
   protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
  }
   /**
    * 放行路径
    * @param web
    * @throws Exception
    */
   @Override
   public void configure(WebSecurity web) throws Exception {
       web.ignoring().antMatchers(
               "/captcha",
               "/login",
               "/logout",
               "/css/**",
               "/js/**",
               "/index.html",
               "favicon.ico",
               "/doc.html",
               "/webjars/**",
               "/swagger-resources/**",
               "/v2/api-docs/**"
              );
  }

   @Override
   protected void configure(HttpSecurity http) throws Exception {
       // 使用JWT,不需要csrf
       http.csrf().disable()
               // 使用JWT,不需要session
              .sessionManagement()
              .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
              .and()
               // 禁用缓存
              .headers()
              .cacheControl();
       ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry requests = http.authorizeRequests();
       List<PerssionPO> mapperAll = perssionMapper.findAll();
       mapperAll.forEach(perssionPO ->
               requests.antMatchers(perssionPO.getUrl()).hasAuthority(perssionPO.getCode()));
       //requests.anyRequest().denyAll().and().headers().cacheControl();
       requests.anyRequest().permitAll();
       // 添加JWT 登录授权过滤器
       http.addFilterBefore(jwtAuthorizationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
       // 添加自定义未授权和未登录结果返回
       http.exceptionHandling()
              .accessDeniedHandler(restfulAccessDeniedHandler)
              .authenticationEntryPoint(restAuthorizationEntryPoint);
  }


}

10.书写测试Controller

import com.zhuoyue.common.RespBean;
import com.zhuoyue.mapper.UserMapper;
import com.zhuoyue.po.UserPO;
import com.zhuoyue.servcie.UserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.security.Principal;

@RestController
public class TestController {
   @Resource
   private UserService userService;
   @Resource
   private UserMapper userMapper;
   @PostMapping("/login")
   public RespBean login(String username,String password){
       return userService.login(username,password);
  }
   @PostMapping("/r/r")
   public String r(){
       return "我是r,我不要权限";
  }
   @PostMapping("/r/r1")
   public String r1(){
       return "我是r1,我需要p1权限";
  }
   @PostMapping("/r/r2")
   public String r2(){
       return "我是r2,我需要p3权限";
  }
   @PostMapping("/r/r3")
   public String r3(){
       return "我是r3,我不需要权限";
  }

   @GetMapping("/r/info")
   public UserPO getAdminInfo(Principal principal) {
       if (principal != null) {
           String username = principal.getName();
           UserPO user = userMapper.findUserByUserName(username);
           // 将用户名密码设置null,安全性。
           user.setPassword(null);
           return user;
      }
       return null;
  }
}
 

标签:集成,return,String,JWT,springframework,org,import,Security,security
来源: https://www.cnblogs.com/WangJingjun/p/16615565.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有