1)JWT简介
https://blog.csdn.net/weixin_42338555/article/details/82776069
2)使用springboot集成jwt
pom引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
JWTLoginFilter
public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JWTLoginFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
// 接收并解析用户凭证
@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException {
try {
User user = new ObjectMapper().readValue(req.getInputStream(), User.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
user.getUsername(),
user.getPassword(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 用户成功登录后,这个方法会被调用,我们在这个方法里生成token
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities();
// 定义存放角色集合的对象
List roleList = new ArrayList<>();
for (GrantedAuthority grantedAuthority : authorities) {
roleList.add(grantedAuthority.getAuthority());
}
String token = Jwts.builder()
.setSubject(authResult.getName() + "-" + roleList)
.setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
.signWith(SignatureAlgorithm.HS512, ConstantKey.SIGNING_KEY)
.compact();
response.addHeader("Authorization", JwtUtils.getTokenHeader(token));
}
}
JWTAuthenticationFilter
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 header = request.getHeader("Authorization");
if (header == null || !header.startsWith(JwtUtils.getAuthorizationHeaderPrefix())) {
chain.doFilter(request, response);
return;
}
UsernamePasswordAuthenticationToken authenticationToken = getUsernamePasswordAuthenticationToken(header);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getUsernamePasswordAuthenticationToken(String token) {
String user = Jwts.parser()
.setSigningKey(ConstantKey.SIGNING_KEY)
.parseClaimsJws(token.replace(JwtUtils.getAuthorizationHeaderPrefix(), ""))
.getBody()
.getSubject();
if (null != user) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
}
SecurityConfiguration
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
/**
* 需要放行的URL
*/
private static final String[] AUTH_WHITELIST = {
"/users/signup"
};
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(AUTH_WHITELIST).permitAll()
.anyRequest().authenticated()// 所有请求需要身份认证
.and()
.addFilter(new JWTLoginFilter(authenticationManager()))
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.logout() // 默认注销行为为logout,可以通过下面的方式来修改
.logoutUrl("/logout")
.logoutSuccessUrl("/login")// 设置注销成功后跳转页面,默认是跳转到登录页面;
.permitAll();;
}
// 该方法是登录的时候会进入
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
// 使用自定义身份验证组件
auth.authenticationProvider(new CustomAuthenticationProvider(userDetailsService, bCryptPasswordEncoder));
}
自定义身份认证验证组件
public class CustomAuthenticationProvider implements AuthenticationProvider {
private UserDetailsService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public CustomAuthenticationProvider(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder){
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 获取认证的用户名 & 密码
String name = authentication.getName();
String password = authentication.getCredentials().toString();
// 认证逻辑
UserDetails userDetails = userDetailsService.loadUserByUsername(name);
if (null != userDetails) {
if (bCryptPasswordEncoder.matches(password, userDetails.getPassword())) {
//这里设置权限和角色
ArrayList<GrantedAuthority> authorities = new ArrayList<>();
authorities.add( new CustomGrantedAuthority("ROLE_ADMIN"));
authorities.add( new CustomGrantedAuthority("AUTH_WRITE"));
// 生成令牌 这里令牌里面存入了:name,password,authorities, 当然你也可以放其他内容
Authentication auth = new UsernamePasswordAuthenticationToken(name, password, authorities);
return auth;
}else {
throw new BadCredentialsException("密码错误");
}
}else {
throw new UsernameNotFoundException("用户不存在");
}
}
/**
* 是否可以提供输入类型的认证服务
*/
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
3)验证JWT结果
1. 注册用户
http://localhost:8080/users/signup
{
"username":"payne",
"password":"1234"
}
2. 登录用户
http://localhost:8080/login
{
"username": "payne",
"password": "1234"
}
3. 测试API
Key :Authorization
Value:根据上一步骤获取
当使用错误token则验证不通过