最后
这份文档从构建一个键值数据库的关键架构入手,不仅带你建立起全局观,还帮你迅速抓住核心主线。除此之外,还会具体讲解数据结构、线程模型、网络框架、持久化、主从同步和切片集群等,帮你搞懂底层原理。相信这对于所有层次的Redis使用者都是一份非常完美的教程了。
整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
你的支持,我的动力;祝各位前程似锦,offer不断!!!
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, “/**”).permitAll()
// 允许对于网站静态资源的无授权访问
.antMatchers(
HttpMethod.GET,
“/”,
“/*.html”,
“/favicon.ico”,
“/**/*.html”,
“/**/*.css”,
“/**/*.js”
).permitAll()
// 授权接口放通token校验
.antMatchers(“/authority/**/authorization/”).permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
// 添加JWT filter
httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
// 禁用缓存
httpSecurity.headers().cacheControl();
}
}
Web配置文件中我们可以看到,还需要UserDetailsService和JwtAuthenticationTokenFilter。UserDetailsService是Spring Security内部接口,我们需要实现该接口的loadUserByUsername方法,将查询到username和password返回,具体代码如下所示:
@Slf4j
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
private TamadbUserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
TamadbUser userPo = userMapper.getUserBaseInfo(Integer.valueOf(userName));
if (userPo == null) {
log.error(“loadUserByUsername—>userName:{}不存在”, userName);
throw new UsernameNotFoundException(“用户名不存在”);
}
SysUserPo user = new SysUserPo();
user.setUsername(userPo.getId() + “”);
user.setPassword(userPo.getPassword());
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
final String rawPassword = user.getPassword();
user.setPassword(encoder.encode(rawPassword));
return user;
}
}
userMapper.getUserBaseInfo方法就是一个dao,用来查询数据库的用户信息,因为WebSecurityConfig配置文件,对密码配置了BCryptPasswordEncoder加密,但是数据库存储的是md5生成的密码,所以我们需要对密码进行等价加密。
我们接着来看一下JwtAuthenticationTokenFilter过滤器的内容:
@Component
@Log4j2
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired(required = false)
private UserDetailsService userDetailsService;
@Value(“${jwt.header}”)
private String header;
@Value(“${jwt.tokenHead}”)
private String tokenHead;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String authHeader = request.getHeader(this.header);
if (authHeader != null && authHeader.startsWith(tokenHead) && authHeader.length() > tokenHead.length() + 1) {
// The part after "Bearer "
final String authToken = authHeader.substring(tokenHead.length() + 1);
String username = jwtTokenUtil.getUsernameFromToken(authToken);
log.info(“checking authentication,username:{},authToken:{}”, username, authToken);
// 校验token是否有效合法
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
// 校验token是否过期
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(
request));
log.info("authenticated user " + username + “, setting security context”);
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
log.error(“token过期,token:{}”, authToken);
}
} else {
log.error(“token失效,无法获取到用户信息,token:{}”, authHeader);
}
}
chain.doFilter(request, response);
}
}
过滤器就做一件事情,获取Http头部的Token信息,然后通过jwtTokenUtil解密Token,获取用户信息,最后检验Token是否过期。
我们最后来看看jwtTokenUtil工具类中,是如何生成、解密Token的。
@Component
@Log4j2
public class JwtTokenUtil implements Serializable {
private static final long serialVersionUID = -3301605591108950415L;
/**
- 用户id
*/
private static final String CLAIM_KEY_USERNAME = “sub”;
/**
- 用户登录信息
*/
private static final String AUTHORITY_USER_DETAIL = “detail”;
/**
- token创建时间
*/
private static final String CLAIM_KEY_CREATED = “created”;
@Value(“${jwt.secret}”)
private String secret;
@Value(“${jwt.expiration.pc.access}”)
private Long pcAccessExpiration;
@Value(“${jwt.expiration.pc.refresh}”)
private Long pcRefreshExpiration;
@Value(“${jwt.expiration.wechat.access}”)
private Long weChatAccessExpiration;
@Value(“${jwt.expiration.wechat.refresh}”)
private Long weChatRefreshExpiration;
/**
-
获取用户token
-
@param token
-
@return
*/
public String getUsernameFromToken(String token) {
String username;
try {
final Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
/**
-
获取用户token
-
@param token
-
@return
*/
public AuthorityUserDto getUserDetailFromToken(String token) {
AuthorityUserDto detail;
try {
final Claims claims = getClaimsFromToken(token);
Object detailObject = claims.get(AUTHORITY_USER_DETAIL);
Gson gson = new Gson();
// 解析json
detail = gson.fromJson(gson.toJson(detailObject), AuthorityUserDto.class);
} catch (Exception e) {
detail = null;
}
return detail;
}
/**
-
获取token的创建时间
-
@param token
-
@return
*/
public Date getCreatedDateFromToken(String token) {
Date created;
try {
final Claims claims = getClaimsFromToken(token);
created = new Date((Long) claims.get(CLAIM_KEY_CREATED));
} catch (Exception e) {
created = null;
}
return created;
}
/**
-
获取token的过期时间
-
@param token
-
@return
*/
public Date getExpirationDateFromToken(String token) {
Date expiration;
try {
final Claims claims = getClaimsFromToken(token);
expiration = claims.getExpiration();
} catch (Exception e) {
expiration = null;
}
return expiration;
}
/**
-
调用jar生成token令牌
-
@param token
-
@return
*/
private Claims getClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
log.error(“解析token是失败:错误信息:{}”, com.fourkmiles.common.util.ExceptionUtil.formatException(e));
claims = null;
}
return claims;
}
/**
-
生成过期时间
-
@param tokenExpirationDto
-
@return
*/
private Date generateExpirationDate(TokenExpirationDto tokenExpirationDto) {
Date expirationDate = null;
ChannelEnum channelEnum = tokenExpirationDto.getChannelEnum();
TokenEnum tokenEnum = tokenExpirationDto.getTokenEnum();
if (channelEnum.getType() == ChannelEnum.PC_CHANNEL.getType()) {
if (tokenEnum.getType() == TokenEnum.ACCESS_TOKEN.getType()) {
expirationDate = new Date(System.currentTimeMillis() + pcAccessExpiration * 1000);
} else {
expirationDate = new Date(System.currentTimeMillis() + pcRefreshExpiration * 1000);
}
} else {
if (tokenEnum.getType() == TokenEnum.ACCESS_TOKEN.getType()) {
expirationDate = new Date(System.currentTimeMillis() + weChatAccessExpiration * 1000);
} else {
expirationDate = new Date(System.currentTimeMillis() + weChatRefreshExpiration * 1000);
}
}
return expirationDate;
}
/**
-
校验token是否过期
-
@param token
-
@return
*/
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
/**
-
生成token
-
@param userDetails
-
@param tokenExpirationDto
-
@return
*/
public String generateToken(UserDetails userDetails, TokenExpirationDto tokenExpirationDto, AuthorityUserDto authorityUserDto) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
claims.put(AUTHORITY_USER_DETAIL, authorityUserDto);
return generateToken(claims, tokenExpirationDto);
}
/**
-
生成token
-
@param claims
-
@param tokenExpirationDto
-
@return
*/
String generateToken(Map<String, Object> claims, TokenExpirationDto tokenExpirationDto) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate(tokenExpirationDto))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
/**
-
是否具备刷新条件
-
@param token
-
@return
*/
public Boolean canTokenBeRefreshed(String token) {
return !isTokenExpired(token);
}
/**
-
刷新token
-
@param refreshAuthorityQuery
-
@param tokenExpirationDto
-
@return
*/
public String refreshToken(RefreshAuthorityQuery refreshAuthorityQuery, TokenExpirationDto tokenExpirationDto) {
String refreshedToken;
try {
final Claims claims = getClaimsFromToken(refreshAuthorityQuery.getToken());
claims.put(CLAIM_KEY_CREATED, new Date());
refreshedToken = generateToken(claims, tokenExpirationDto);
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
}
/**
-
校验token是否合法
-
@param token
-
@param userDetails
-
@return
*/
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
final Date created = getCreatedDateFromToken(token);
return !isTokenExpired(token);
}
}
最后
s, tokenExpirationDto);
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
}
/**
-
校验token是否合法
-
@param token
-
@param userDetails
-
@return
*/
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
final Date created = getCreatedDateFromToken(token);
return !isTokenExpired(token);
}
}
最后
[外链图片转存中…(img-2LuEcH5D-1715549024223)]