Token到底如何实现,才是最好的方式?
身经百个项目(.(▼へ▼メ)装逼.) 见过不少项目是这样实现的
今天要讲解的是采用 JWT ,解决Token问题
JSON Web Token (JWT)是在网络应用间传递信息的一种基于JSON的开放标准 (RFC 7519) ,用于作为JSON对象在不同系统之间进行安全地信息传输。主要使用场景一般是用来在 身份提供者和服务提供者间传递被认证的用户身份信息。
JWT由三部分组成,依次如下:
有JWT解决Token问题,再结合Spring Security 可以解决用户权限模块重重问题(真的很强大),当然除了Spring Security,还有Apache Shiro也可以解决权限,但没有Security强大,后续将写篇文章详解对比两者。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
复制代码
@Component
public class JwtTokenUtil {
private static final String CLAIM_KEY_USERNAME = "sub";
private static final String CLAIM_KEY_CREATED = "created";
// 生成token
public String generateToken(UserDetails userDetails) {
...
}
// 刷新token
public String refreshToken(String token) {
...
}
// 校验token
public Boolean validateToken(String token, UserDetails userDetails) {
...
}
复制代码
@Component
public class JwtTokenFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String authHeader = request.getHeader(Constant.HEADER_STRING );
if (authHeader != null && authHeader.startsWith(Constant.TOKEN_PREFIX )) {
final String authToken = authHeader.substring(Constant.TOKEN_PREFIX.length() );
String username = jwtTokenUtil.getUsernameFromToken(authToken);
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());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(
request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
chain.doFilter(request, response);
}
}
复制代码
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userService;
@Bean
public JwtTokenFilter authenticationTokenFilterBean() throws Exception {
return new JwtTokenFilter();
}
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
String[] urls = new String[]{"/user/login", "/user/register"};
httpSecurity.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
// 允许注册和登录接口不需token访问
.antMatchers(urls).permitAll()
.anyRequest().authenticated();
httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
httpSecurity.headers().cacheControl();
}
}
复制代码
@RestController
public class RoleController {
/**
* 测试普通权限
*
* @return
*/
@PreAuthorize("hasAuthority('ROLE_NORMAL')")
@GetMapping(value="/normal/test")
public String test1() {
return "普通角色访问";
}
/**
* 测试管理员权限
*
* @return
*/
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
@GetMapping(value = "/admin/test")
public String test2() {
return "管理员访问";
}
}
复制代码
需给用户添加角色才能访问,如下图 用户角色关系表