SpringBoot2.7.X整合SpringSecurity+JWT、入门级简单易懂
- 前言
- 思路
- 导入相关依赖
- 整合security
- 建用户表
- 实体类UserDetailsInfo
- 创建Mapper
- 创建UserDetailsServiceImpl
- 配置 Security
- 写一个登录接口进行测试
- 整合jwt
- 创建jwt工具类
- 创建jwt过滤器
- 使用自带缓存用于token刷新机制
- 两个异常类
- 测试接口
前言
主要是自己想学习一下,也分享给大家,主要参考 链接: link 在这里我不会去介绍SpringSecurity 和JWT,直接上代码的形式,不了解的可以去了解一下
已经更新了,代码已完整、可以正常运行
有问题可以留言,对你有帮助请点个赞,让更多人学习到。
思路
1、先整合security:
使用自己数据库的用户,来登录认证(说一下我没有用到角色权限)
2、整合jwt:
能登录认证了,那就需要生成token
3、过滤器Filter:
每次调用接口时认证token合法性
4、jwt刷新机制:
我用的是缓存刷新机制,我这里弄的比较简单用的是SpringBoot自带的缓存
5、扩展:
比如token生成使用的秘钥可以使用RAS私钥公钥,提高安全性。缓存可以使用redis
在这里我就不使用了。
导入相关依赖
主要的依赖是 spring-boot-starter-web、spring-boot-starter-security、jjwt
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 常用工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<!-- json工具 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.7</version>
</dependency>
<!-- log4日志 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
<version>2.7.9</version>
</dependency>
<!-- JJWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
整合security
建用户表
直接建立一个简单的用户表
用户id | 用户名 | 用户名 |
userid | username | password |
实体类UserDetailsInfo
给用户表创建实体类,并实现UserDetails。
UserDetails是security加载用户信息的接口,可以用来身份认证和授权
public class UserDetailsInfo implements UserDetails {
private Integer userid;
private String username;
private String password;
public Integer getUserid() {
return userid;
}
public void setUserid(Integer userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username == null ? null : username.trim();
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password == null ? null : password.trim();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
//判断用户是否过期
@Override
public boolean isAccountNonExpired() {
return true;
}
/*
*判断用户是否被锁定
* 一般用于多次登录失败。
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/*
*用于判断用户的凭证(密码)是否过期
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/*
*判断用户是否被禁用
*/
@Override
public boolean isEnabled() {
return true;
}
}
创建Mapper
用于访问用户表
1、UserDetailsMapper
@Repository
public interface UserDetailsMapper {
UserDetailsInfo getUserInfoByUsername(String username);
}
2、UserDetailsMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.stc.login.mapper.UserDetailsMapper">
<resultMap id="BaseResultMap" type="com.stc.login.model.UserDetailsInfo">
<id column="UserId" jdbcType="INTEGER" property="userid" />
<result column="Username" jdbcType="NVARCHAR" property="username" />
<result column="Password" jdbcType="NVARCHAR" property="password" />
</resultMap>
<sql id="Base_Column_List">
UserId, Username, Password
</sql>
<select id="getUserInfoByUsername" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from STC_EmployeePassWord
where Username = #{username,jdbcType=NVARCHAR}
</select>
</mapper>
创建UserDetailsServiceImpl
UserDetailsServiceImpl 是一个实现了 UserDetailsService 接口的类,它可以用来自定义从数据库中获取用户信息的逻辑。
UserDetailsService 接口只有一个方法 loadUserByUsername ,它接收一个用户名作为参数,返回一个 UserDetails 对象,表示用户的核心信息,包括用户名、密码、权限等。
我这里密码是明码,所以把数据库密码取出来在加密的,不然你的安全机制使用了加密,数据库密码又不加密会认证失败的(passwordEncoder.encode)
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserDetailsMapper userDetailsMapper;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetailsInfo userDetailsInfo = userDetailsMapper.getUserInfoByUsername(username);
userDetailsInfo.setPassword( passwordEncoder.encode(userDetailsInfo.getPassword()));
if(userDetailsInfo == null){
throw new UsernameNotFoundException("用戶不存在");
}
return userDetailsInfo;
}
}
配置 Security
SecurityConfig 是一个配置类,它可以用来自定义 Spring Security 的配置,包括认证、授权、跨域、异常处理等。
注意:spring boot2.7以上版本不需要继承WebSecurityConfigurerAdapter 类,直接使用@Bean注解
注意2:我这里就直接把完整的代码贴上来,已经加上了jwtAuthenticationFilter 和异常处理
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private JWTAuthenticationFilter jwtAuthenticationFilter;
@Autowired
private MyAuthenticationEntryPoint myAuthenticationEntryPoint;
@Autowired
private MyAccessDeniedHandler myAccessDeniedHandler;
/**
* 密码明文加密方式配置
*默认加密方式
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 获取AuthenticationManager(认证管理器),登录时认证使用
* 默认认证
* @param authenticationConfiguration
* @return
* @throws Exception
*/
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable(); // 基于 token,不需要 csrf
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);// 基于 token,不需要 session
// 下面开始设置权限
http.authorizeRequests(authorize -> authorize
.antMatchers("/stc/login/login.action").permitAll()不需要进行身份验证的接口
.anyRequest().authenticated()//除上面外的所有请求全部需要 鉴权认证
);
//定义filter的先后顺序,保证 jwtFilter比用户验证的过滤器先执行
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
//自定义异常捕获机制
http.exceptionHandling().authenticationEntryPoint(myAuthenticationEntryPoint).accessDeniedHandler(myAccessDeniedHandler);
return http.build();
}
}
写一个登录接口进行测试
1、LoginAction
@RestController /* @RestController 是 @ResponseBody 和 @Controller 的组合注解 */
@RequestMapping("/stc/login")
public class LoginAction {
@Autowired
private LoginService loginService;
@RequestMapping(value = {"login.action"}, method = RequestMethod.POST)
public Map<String, String> login(@RequestBody JSONObject jsonObject) {
return loginService.login(jsonObject);
}
}
2、LoginService
public interface LoginService {
public Map<String, String> login(JSONObject jsonObject);
}
3、LoginServiceImp
@Service
public class LoginServiceImp implements LoginService {
private static final Logger log = LoggerFactory.getLogger(LoginServiceImp.class);
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserTokenCacheService userTokenCacheService;
@Autowired
private AuthenticationManager authenticationManager;
@Override
public Map<String, String> login(JSONObject jsonObject) {
Map<String, String> loginMap = new HashMap<>();
//封装 Authentication
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(jsonObject.get("username"), jsonObject.get("password"));
//认证用户
Authentication authenticate = authenticationManager.authenticate(authenticationToken);
if (authenticate == null) {
throw new RunException("认证失败!");
}
//获取认证用户信息
UserDetailsInfo userDetailsInfo = (UserDetailsInfo) authenticate.getPrincipal();
//认证通过,生成jwt
String token = jwtTokenUtil.generateToken(userDetailsInfo);
loginMap.put("userid", String.valueOf(userDetailsInfo.getUserid()));
loginMap.put("username", userDetailsInfo.getUsername());
loginMap.put("token", token);
//存入缓存中
userTokenCacheService.userTokenCachePut(loginMap);
return loginMap;
}
}
我已经把完整的代码贴出来了,包含了生成token,存入缓存,把生成好的token返回给客户端端,其他的接口就直接token认证
整合jwt
创建jwt工具类
@Component
public class JwtTokenUtil {
private static final Logger log = LoggerFactory.getLogger(JwtTokenUtil.class);
public static final String TOKEN_HEADER = "x-access-token"; //token请求头
public static final String TOKEN_PREFIX = "Bearer";//token前缀
private static final String ISSUER = "STC"; //发行方
private static final String SUBJECT = "OMM"; //签名主题
private static final String AUDIENCE = "OMM-VUE"; //接收方
private String ROLE_CLAIMS = "role"; //角色权限声明
private String secret = "secret"; //jwt加解密使用的密钥;
private int expiration = 60; //jwt过期时间 秒级别
private static final String CLAIM_KEY_USERNAME = "username";//登录的用户号
private static final String CLAIM_KEY_CREATED = "created";// jwt创建时间
/**
* 生成token的过期时间
*/
private Date generateExpirationDate() {
//毫秒级
return new Date(System.currentTimeMillis() + expiration * 1000);
}
/**
* 根据用户信息生成token
*/
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());//用户名
claims.put(CLAIM_KEY_CREATED, new Date());//创建日期
return Jwts.builder()
.setIssuer(ISSUER)//发行方
.setSubject(SUBJECT)//主题
.setAudience(AUDIENCE)//接收方
.setClaims(claims)//其他信息,主要是用户信息
.setIssuedAt(new Date())//签发日期
.setNotBefore(new Date())//生效日期
.setExpiration(generateExpirationDate())//失效时间
.signWith(SignatureAlgorithm.HS512, secret)//生成算法
.compact();
}
/**
* 从token中获取JWT中的有效载荷
*/
public Claims getClaimsFromToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (ExpiredJwtException e) {
// 捕获过期异常
log.info("token:" + token);
log.info("token已经过期:" + e.getMessage());
claims = e.getClaims();
} catch (Exception e) {
log.info("token:" + token);
log.info("JWT格式验证失败:" + e.getMessage());
}
return claims;
}
/**
* 从token中获取登录用户名
*/
public String getUserNameFromToken(String token) {
Claims claims = getClaimsFromToken(token);
return claims.get("username").toString();
}
/**
* 从token中获取过期时间
*/
public Date getExpiredDateFromToken(String token) {
Claims claims = getClaimsFromToken(token);
return claims.getExpiration();
}
/**
* 判断token是否已经失效
*/
public boolean isTokenExpired(String token) {
Date expiredDate = getExpiredDateFromToken(token);
return expiredDate.before(new Date());
}
}
创建jwt过滤器
JWTAuthenticationFilter 是一个自定义的过滤器类,它继承了 OncePerRequestFilter 类,用于实现 JWT 的验证机制。
JWTAuthenticationFilter 需要重写 doFilterInternal 方法,用于检查请求头中是否有 Authorization 字段,并验证 JWT 的正确性,然后将认证信息设置到 SecurityContext 中
JWTAuthenticationFilter 需要在 SecurityConfig 中通过 addFilterBefore 方法添加到过滤器链中,并指定在 UsernamePasswordAuthenticationFilter 之前执行。
@Component
public class JWTAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserTokenCacheService userTokenCacheService;
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//需要认证token的接口才进入
if(!notNeedFilter(request)){
// 获取 token
String token = request.getHeader(jwtTokenUtil.TOKEN_HEADER);
if (StringUtils.hasText(token)) {
// 解析token,获取 username
String username = jwtTokenUtil.getUserNameFromToken(token);
//判断是否有该username 的缓存,(登录存入缓存,注销清除缓存)
userTokenCacheInfo userTokenCacheInfo = userTokenCacheService.getUserTokenCacheById(username);
if (userTokenCacheInfo != null) {
//判断存储的token跟传入的token是否一致,
String CToken = userTokenCacheInfo.getToken();
if (CToken.equals(token)) {
//判断token是否过期
if (jwtTokenUtil.isTokenExpired(token)) {
//判断是否超过最大失效时间
if (userTokenCacheInfo.getMaxExpirationDate().before(new Date())) {
ResponseProcessing(request, response, "exceeding-maximum-failure-time", null);
}
//是否超过设定时间没有操作
else if (DateUtils.addMinutes(userTokenCacheInfo.getOldOperationDate(), userTokenCacheInfo.getInactiveMinute()).before(new Date())) {
ResponseProcessing(request, response, "exceeding-inactive-time", null);
}
//满足条件 活跃用户,又没有超过最大失效时间,从新生成token给客户端
else {
UserDetailsInfo userDetailsInfo = new UserDetailsInfo();
userDetailsInfo.setUsername(username);
//生成jwt
String newToken = jwtTokenUtil.generateToken(userDetailsInfo);
//更新缓存
userTokenCacheService.userTokenCacheUpdate(newToken, username);
ResponseProcessing(request, response, "renovate-token", newToken);
}
} else {
//用户是否授权
if (SecurityContextHolder.getContext().getAuthentication() == null) {
//将User 封装到 securityContextHolder。 封装到securityContextHolder以后,其他过滤器就不会在拦截
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userTokenCacheInfo, null, null);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
//认证成功,更改缓存里面的调用接口时间,主要用于判断是否活跃
userTokenCacheService.userTokenCacheOldOperationDateUpdate(userTokenCacheInfo, username);
}
}
}
}
}
filterChain.doFilter(request, response);
}
//直接返回给客户端
private void ResponseProcessing(HttpServletRequest request, HttpServletResponse response, String type, String token) throws IOException {
Map<String, Object> map = new HashMap<>();
map.put("uri", request.getRequestURI());
if (type.equals("exceeding-maximum-failure-time")) {
map.put("msg", "exceeding-maximum-failure-time");
} else if (type.equals("exceeding-inactive-time")) {
map.put("msg", "exceeding-inactive-time");
} else if (type.equals("renovate-token")) {
map.put("msg", "renovate-token");
map.put("token", token);
} else {
map.put("msg", "身份认证失败");
}
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setCharacterEncoding("utf-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
String resBody = JSON.toJSONString(map);
PrintWriter printWriter = response.getWriter();
printWriter.print(resBody);
printWriter.flush();
printWriter.close();
}
/**
* 判断是否可以通过过滤
*/
private boolean notNeedFilter(HttpServletRequest request) {
//登录接口
if ((request.getContextPath() + "/stc/login/login.action").equals(request.getRequestURI())) {
return true;
}
return false;
}
}
我已经把完整的代码贴出,使用到了缓存
我这里的token 使用的是缓存刷新token机制
第一点:如果超过最大的失效日期,需要客户端重新输入用户名和密码认证,发放新的token
第二点:我设置是30分钟不活跃,需要客户端重新输入用户名和密码认证,发放新的token
第三点:不执行上面两点,token过期直接发放新的token,然后客户端获取token重新发送请求。
使用自带缓存用于token刷新机制
包含登录成功调用缓存,更新调用缓存,更新活跃时间调用缓存,注销删除缓存,
Spring boot 自带缓存可以去了解一下。
1、userTokenCacheInfo
public class userTokenCacheInfo {
private String token;
private String username;
private Date loginDate;//登录时间
private Date notBeforeDate; //token生效时间
private Date expirationDate; //token过期时间
private int refreshCount = 0; //token的刷新次数
private Date maxExpirationDate;//token最大的失效时间
private Date OldOperationDate;//上一次调用接口的操作时间
private String equipmentIP;//设备ip
private int InactiveMinute = 30; //默认30分钟,用于如果30分钟不操作,token失效。
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getLoginDate() {
return loginDate;
}
public void setLoginDate(Date loginDate) {
this.loginDate = loginDate;
}
public Date getNotBeforeDate() {
return notBeforeDate;
}
public void setNotBeforeDate(Date notBeforeDate) {
this.notBeforeDate = notBeforeDate;
}
public Date getExpirationDate() {
return expirationDate;
}
public void setExpirationDate(Date expirationDate) {
this.expirationDate = expirationDate;
}
public int getRefreshCount() {
return refreshCount;
}
public void setRefreshCount(int refreshCount) {
this.refreshCount = refreshCount;
}
public Date getMaxExpirationDate() {
return maxExpirationDate;
}
public void setMaxExpirationDate(Date maxExpirationDate) {
this.maxExpirationDate = maxExpirationDate;
}
public Date getOldOperationDate() {
return OldOperationDate;
}
public void setOldOperationDate(Date oldOperationDate) {
OldOperationDate = oldOperationDate;
}
public String getEquipmentIP() {
return equipmentIP;
}
public void setEquipmentIP(String equipmentIP) {
this.equipmentIP = equipmentIP;
}
public int getInactiveMinute() {
return InactiveMinute;
}
public void setInactiveMinute(int inactiveMinute) {
InactiveMinute = inactiveMinute;
}
}
2、UserTokenCacheService
public interface UserTokenCacheService {
public void userTokenCachePut(Map<String, String> loginMap);
public userTokenCacheInfo getUserTokenCacheById(Object key);
public void userTokenCacheUpdate(String token, Object key);
public void userTokenCacheOldOperationDateUpdate(userTokenCacheInfo userTokenCacheInfo, Object key);
public void userTokenCacheEvict(Object key);
}
3、UserTokenCacheServiceImpl
@Service
public class UserTokenCacheServiceImpl implements UserTokenCacheService {
// 注入缓存管理器
@Autowired
private CacheManager cacheManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
// 定义userTokenCache()方法,用于获取或创建"userTokenCache"缓存对象
//用于Token缓存
private Cache userTokenCache() {
// 获取或创建名为"userCache"的缓存对象
Cache cache = cacheManager.getCache("userTokenCache");
return cache;
}
// 从缓存中获取用户数据
@Override
public userTokenCacheInfo getUserTokenCacheById(Object key) {
// 获取"userCache"缓存对象
Cache cache = userTokenCache();
// 从缓存中获取用户数据
userTokenCacheInfo userTokenCacheInfo = cache.get(key, userTokenCacheInfo.class);
return userTokenCacheInfo;
}
//初始化放入缓存中
@Override
public void userTokenCachePut(Map<String, String> loginMap) {
// 获取"userCache"缓存对象
Cache cache = userTokenCache();
// 从缓存中获取用户数据
userTokenCacheInfo userTokenCacheInfo = getUserTokenCacheById(loginMap.get("username"));
if (userTokenCacheInfo == null) {
userTokenCacheInfo = new userTokenCacheInfo();
}
userTokenCacheInfo.setUsername(loginMap.get("username"));
userTokenCacheInfo.setToken(loginMap.get("token"));
//获取token的信息
Claims claims = jwtTokenUtil.getClaimsFromToken(loginMap.get("token"));
userTokenCacheInfo.setLoginDate(claims.getNotBefore());//登录日期
userTokenCacheInfo.setNotBeforeDate(claims.getNotBefore());//生效日期
userTokenCacheInfo.setExpirationDate(claims.getExpiration());//失效日期
userTokenCacheInfo.setRefreshCount(0);//刷新次数
userTokenCacheInfo.setOldOperationDate(claims.getNotBefore());//上一次调用接口的操作时间
userTokenCacheInfo.setMaxExpirationDate(DateUtils.addHours(claims.getNotBefore(), 4));//最大失效时间
// 将用户数据放入缓存中
cache.put(loginMap.get("username"), userTokenCacheInfo);
}
//更新缓存token
@Override
public void userTokenCacheUpdate(String token, Object key) {
// 从缓存中获取用户数据
userTokenCacheInfo userTokenCacheInfo = getUserTokenCacheById(key);
//获取token的信息
Claims claims = jwtTokenUtil.getClaimsFromToken(token);
userTokenCacheInfo.setToken(token);
userTokenCacheInfo.setNotBeforeDate(claims.getNotBefore());//生效日期
userTokenCacheInfo.setExpirationDate(claims.getExpiration());//失效日期
userTokenCacheInfo.setRefreshCount(userTokenCacheInfo.getRefreshCount() + 1);//刷新次数
userTokenCacheInfo.setOldOperationDate(new Date());//上一次调用接口的操作时间
// 获取"userCache"缓存对象
Cache cache = userTokenCache();
// 将用户数据放入缓存中
cache.put(key, userTokenCacheInfo);
}
//更新缓存 调用接口的操作时间
@Override
public void userTokenCacheOldOperationDateUpdate(userTokenCacheInfo userTokenCacheInfo, Object key) {
userTokenCacheInfo.setOldOperationDate(new Date());//上一次调用接口的操作时间
// 获取"userCache"缓存对象
Cache cache = userTokenCache();
// 将用户数据放入缓存中
cache.put(key, userTokenCacheInfo);
}
//删除缓存
@Override
public void userTokenCacheEvict(Object key) {
// 获取"userCache"缓存对象
Cache cache = userTokenCache();
cache.evict(key);
}
}
两个异常类
1、MyAuthenticationEntryPoint
@Component
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
Map<String, Object> map = new HashMap<>();
map.put("uri", httpServletRequest.getRequestURI());
map.put("msg", "身份认证失败");//身份认证失败
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpServletResponse.setCharacterEncoding("utf-8");
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
ObjectMapper objectMapper = new ObjectMapper();
String resBody = objectMapper.writeValueAsString(map);
PrintWriter printWriter = httpServletResponse.getWriter();
printWriter.print(resBody);
printWriter.flush();
printWriter.close();
}
}
2、MyAccessDeniedHandler
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
Map<String, Object> map = new HashMap<>();
map.put("uri",httpServletRequest.getRequestURI());
map.put("msg","鉴权失败");//没有足够的权限访问该资源
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
httpServletResponse.setCharacterEncoding("utf-8");
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
ObjectMapper objectMapper = new ObjectMapper();
String resBody = objectMapper.writeValueAsString(map);
PrintWriter printWriter = httpServletResponse.getWriter();
printWriter.print(resBody);
printWriter.flush();
printWriter.close();
}
}
测试接口
1、登录接口测试获取token
2、缓存机制测试 这里我就直接拿我的客户端测试了
没有token、或者跟缓存的token不一样、直接访问,进入异常类返回身份认证失败
3、token过期了,重新生成token