spring boot + spring security + jwt实现

第一步:加载依赖

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

第二步:配置jwt失效时间等信息 

jwt:
  secret: secret
  expiration: 7200000
  token: Authorization

第三步:配置security

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

  @Autowired
  ApplicationParams params;

  @Autowired
  private JwtUserDetailsService jwtUserDetailsService;

  @Autowired
  JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

  @Autowired
  JwtAuthenticationFilter jwtAuthenticationFilter;

  /**
   * 不需要登录就能访问的URL
   *
   * @param web
   */
  @Override
  public void configure(WebSecurity web) {
    web.ignoring().antMatchers(params.getAntMatchers());
  }

  @Bean
  public PasswordEncoder passwordEncoder(
      @Value("${self-params.user.userSuperPassword}") String userSuperPassword) {
    return SelfPasswordEncoder.create(userSuperPassword);
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
            .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
            .and()
            .authorizeRequests()
            .antMatchers("/login").permitAll()
            .antMatchers(HttpMethod.OPTIONS, "/**").anonymous()
            .anyRequest().authenticated()   // 剩下所有的验证都需要验证
            .and()
            .csrf().disable()                      // 禁用 Spring Security 自带的跨域处理
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    // 定制我们自己的 session 策略:调整为让 Spring Security 不创建和使用 session、
                 
http.addFilterBefore(jwtAuthenticationFilter,UsernamePasswordAuthenticationFilter.class);
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(jwtUserDetailsService);
  }
}

第四步:jwt工具类(包含生产token)

@Component
public class JwtTokenUtil {

    private static final long serialVersionUID = -3301605591108950415L;

    @Value("${jwt.secret}")
    private  String secret;

    @Value("${jwt.expiration}")
    private Long expiration;

    @Value("${jwt.token}")
    private String tokenHeader;

    private Clock clock = DefaultClock.INSTANCE;

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return doGenerateToken(claims, userDetails.getUsername());
    }

    private String doGenerateToken(Map<String, Object> claims, String subject) {
        final Date createdDate = clock.now();
        final Date expirationDate = calculateExpirationDate(createdDate);

        return "Bearer " + Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(createdDate)
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    private Date calculateExpirationDate(Date createdDate) {
        return new Date(createdDate.getTime() + expiration);
    }

    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername())
                && !isTokenExpired(token)
        );
    }

    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token, Claims::getSubject);
    }

    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }

    private Claims getAllClaimsFromToken(String token) {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
    }


    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(clock.now());
    }

    public Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }
}

第五步:login方法生产token并返回给前端

@PostMapping("/login")
  public String login(User user, HttpServletRequest request){
    final UserDetails userDetails = userDetailsService.loadUserByUsername(user.getLoginName());
    final String token = jwtTokenUtil.generateToken(userDetails);
    return ReturnUtil.success(token);
  }



@Override
  public UserDetails loadUserByUsername(String loginName) throws UsernameNotFoundException {
    if (adminAccountName.equals(loginName)) {
//      List<SimpleGrantedAuthority> authorities =
//              getSimpleGrantedAuthorities(
//                      roleService.getAllNoCancel(),
//                      functionService.getAllNoCancel(),
//                      menuService.getAllNoCancel(),
//                      SystemStringEnum.RolePowerEnum.ADMIN.getCode());
      List<GrantedAuthority> authorityList = new ArrayList<>();
      authorityList.add(new SimpleGrantedAuthority("ROLE_USER"));
      UserDetails userDetails = new LoginUserVO(
              adminId,
              adminName,
              adminAccountName,
              passwordEncoder.encode(adminPassword),
              true,
              authorityList);
      return userDetails;
    } else {
      User user = userMapper.loginUser(loginName);
      if (Objects.isNull(user)) {
        throw new UsernameNotFoundException("用户名或密码错误!");
      }
//      return getUserDetails(user);
      return null;
    }
  }

第六步:jwt过滤器验证token,通过后把token保存上下文

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final UserDetailsService userDetailsService;
    private final JwtTokenUtil jwtTokenUtil;
    private final String tokenHeader;

    public JwtAuthenticationFilter(@Qualifier("jwtUserDetailsService") UserDetailsService userDetailsService,
                                       JwtTokenUtil jwtTokenUtil, @Value("${jwt.token}") String tokenHeader) {
        this.userDetailsService = userDetailsService;
        this.jwtTokenUtil = jwtTokenUtil;
        this.tokenHeader = tokenHeader;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        final String requestHeader = request.getHeader(this.tokenHeader);
        String username = null;
        String authToken = null;
        if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
            authToken = requestHeader.substring(7);
            try {
                username = jwtTokenUtil.getUsernameFromToken(authToken);
            } catch (ExpiredJwtException e) {
            }
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {

            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);

            if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }

        }
        chain.doFilter(request, response);
    }
}

搞定!前端每次请求时把token放到header中就可以了。更加方便做单点登陆,分布式了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值