Spring-security使用jjwt认证
2018-04-02 本文已影响0人
第八号灬当铺
直接贴几个类的代码
如果报红 估计是 导包问题
可以根据Token Subject的不同实现不同用户的身份认证
提供不同的token接口 生成不同主题的token就行了
过滤器中可以实现
--未实现token刷新功能
--未实现token过期功能
1. pom依赖
<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>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
2. SecurityJjwtDemoApplication项目启动类 也是接口类
提供两个接口 getToken 和 获取当前会话用户
getToken的响应头中有token
获取会话用户需要携带token访问
@SpringBootApplication
@RestController
public class SecurityJjwtDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityJjwtDemoApplication.class, args);
}
@Autowired
private AuthService authService;
@GetMapping("/getToken")
public ResponseEntity<String> createAuthenticationToken(String username, String password) throws JsonProcessingException {
String token = authService.login(username, password);
HttpHeaders headers = new HttpHeaders();
headers.add("X-Authorization", token);
return ResponseEntity.noContent().headers(headers).build();
}
@GetMapping("/currentUser")
public ResponseEntity<UserDetails> currentUser() {
UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return ResponseEntity.ok(userDetails);
}
}
3. WebSecurityConfig配置类
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthService authService;
@Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationTokenFilter(authService);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/favicon.ico");
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// 由于使用的是JWT,我们这里不需要csrf
.csrf().disable()
// 认证过程 发生异常处理类
.exceptionHandling().authenticationEntryPoint(new JwtAuthenticationEntryPoint())
.and()
.headers().httpStrictTransportSecurity().disable()
.and()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 对于获取token的rest api要允许匿名访问
.antMatchers("/getToken").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
// 添加JWT filter
httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
// 禁用缓存
httpSecurity.headers().cacheControl();
}
}
4. JwtAuthenticationEntryPoint异常处理类
@Slf4j
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = -8970718410437077606L;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
log.error("认证授权过程发生异常 返回401状态码", authException);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
5. JwtAuthenticationTokenFilter认证过滤器
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
private AuthService authService;
public JwtAuthenticationTokenFilter(AuthService authService) {
this.authService = authService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String authToken = request.getHeader("X-Authorization".toLowerCase());
if (StringUtils.isNotBlank(authToken)) {
if (SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = authService.getUserDetails(authToken);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
logger.info("authenticated user " + userDetails.getUsername() + ", setting security context");
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
}
6. AuthService服务类
@Service
public class AuthService implements UserDetailsService {
/**
* 密钥
*/
public static final String SECRET = "SECRET";
public static final String USER_KEY = "USER_KEY";
/**
* 用户主题
*/
public static final String USER_SUBJECT = "USER_SUBJECT";
/**
* 过期时间
*/
public static final Long expiration = 604800L;
@Autowired
private ObjectMapper objectMapper;
/**
* 解码出token中的UserDetails
*
* @param authToken
* @return
*/
public UserDetails getUserDetails(String authToken) {
Claims mySecret = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(authToken).getBody();
String subject = mySecret.getSubject();
String user = (String) mySecret.get(USER_KEY);
try {
/**
* 根据不同的用户主题 反序列化出 不同的用户对象
*/
if (USER_SUBJECT.equals(subject)) {
return objectMapper.readValue(user, User.class);
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public String login(String username, String password) throws JsonProcessingException {
UserDetails userDetails = loadUserByUsername(username);
if (!userDetails.getPassword().equals(password)) {
throw new BadCredentialsException("密码错误");
}
Map<String, Object> claims = new HashMap<>();
claims.put(USER_KEY, objectMapper.writeValueAsString(userDetails));
Date issuedAt = new Date();
return Jwts.builder()
.setClaims(claims)
// .setId()
// 主题
.setSubject(USER_SUBJECT)
// 发行时间
.setIssuedAt(issuedAt)
// 过期时间
.setExpiration(new Date(issuedAt.getTime() + expiration * 1000))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
}
/**
* 从数据源中 获取用户数据
*
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = new User().setUsername(username).setPassword("123456");
return user;
}
}
7. UserDetails实现类
@Data
@Accessors(chain = true)
public class User implements UserDetails {
private String username;
private String password;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public boolean isAccountNonExpired() {
return false;
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return false;
}
}