Shiro 返回 Token 给前端的方法
在使用 Apache Shiro 进行权限管理时,通过自定义过滤器、使用 JWT 进行 Token 验证、在登录成功时生成 Token 并返回前端 是实现这一功能的主要方法。下面将详细描述如何通过这些步骤来返回 Token 给前端。
一、理解 Shiro 和 JWT
Shiro 简介
Apache Shiro 是一个强大且灵活的 Java 安全框架,提供了认证、授权、会话管理和加密等功能。它的设计简洁,易于集成到现有应用中。
JWT 简介
JSON Web Token(JWT)是一种开放标准(RFC 7519),它定义了一种紧凑且独立的方式,用于在各方之间作为 JSON 对象安全地传输信息。这个信息可以被验证和信任,因为它是数字签名的。
二、通过自定义过滤器返回 Token
在 Shiro 中,可以通过自定义过滤器来实现复杂的认证逻辑,并在认证成功时生成并返回 Token。
1、自定义过滤器
首先,我们需要创建一个自定义的过滤器,继承 BasicHttpAuthenticationFilter
,并在 onLoginSuccess
方法中生成 Token 并返回给前端。
public class JwtAuthFilter extends BasicHttpAuthenticationFilter {
@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
HttpServletResponse httpResponse = (HttpServletResponse) response;
String jwtToken = JwtUtil.generateToken(subject.getPrincipal().toString());
httpResponse.setHeader("Authorization", "Bearer " + jwtToken);
return true;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
// Custom logic for handling access denied
return true;
}
}
2、配置 Shiro 过滤器
接下来,需要在 Shiro 的配置类中添加自定义过滤器,并将其应用到需要保护的路径。
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/", "jwt");
Map<String, Filter> filters = new HashMap<>();
filters.put("jwt", new JwtAuthFilter());
shiroFilterFactoryBean.setFilters(filters);
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
}
三、使用 JWT 进行 Token 验证
在用户成功登录后,生成一个 JWT 并返回给前端。前端在后续请求中将这个 Token 放在 HTTP 头部,从而实现无状态的用户认证。
1、生成 JWT
使用一个工具类来生成和验证 JWT,工具类可以使用 io.jsonwebtoken
库来实现。
public class JwtUtil {
private static final String SECRET_KEY = "your_secret_key";
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + 86400000)) // 1 day expiration
.signWith(SignatureAlgorithm.HS512, SECRET_KEY)
.compact();
}
public static Claims getClaimsFromToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
public static boolean validateToken(String token, String username) {
final String tokenUsername = getClaimsFromToken(token).getSubject();
return (tokenUsername.equals(username) && !isTokenExpired(token));
}
private static boolean isTokenExpired(String token) {
final Date expiration = getClaimsFromToken(token).getExpiration();
return expiration.before(new Date());
}
}
2、前端接收 Token 并存储
在前端接收到服务器返回的 Token 后,通常会将其存储在 LocalStorage 或 SessionStorage 中,以便在后续请求中使用。
fetch('/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username: 'user', password: 'pass' })
})
.then(response => response.headers.get('Authorization'))
.then(token => {
localStorage.setItem('token', token);
})
.catch(error => console.error('Error:', error));
四、在请求中使用 Token
前端在发送请求时,将 Token 添加到 HTTP 头部,从而实现无状态认证。
fetch('/api/protected', {
method: 'GET',
headers: {
'Authorization': localStorage.getItem('token')
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
五、处理 Token 验证
在后端,通过自定义过滤器来验证 Token 的有效性和合法性。
public class JwtAuthFilter extends BasicHttpAuthenticationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
try {
String token = getAuthzHeader(request);
if (token == null) {
return false;
}
Claims claims = JwtUtil.getClaimsFromToken(token);
if (claims == null || JwtUtil.isTokenExpired(token)) {
return false;
}
String username = claims.getSubject();
if (username == null) {
return false;
}
return true;
} catch (Exception e) {
return false;
}
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpResponse.getWriter().write("Unauthorized");
return false;
}
}
六、总结与推荐
通过以上步骤,使用 Apache Shiro 和 JWT 可以实现安全、无状态的用户认证。自定义过滤器、生成并返回 JWT、验证 Token 有效性 是实现这一功能的关键环节。
在项目团队管理系统的描述中,推荐使用以下两个系统:
通过这些工具,可以进一步提升团队的工作效率和项目管理水平。
相关问答FAQs:
1. 如何在Shiro中将令牌(Token)返回给前端?
Shiro是一个强大的Java安全框架,用于身份验证、授权和会话管理。在Shiro中,将令牌返回给前端可以通过以下步骤完成:
- 首先,用户在前端登录页面输入用户名和密码,并点击登录按钮。
- 然后,前端将用户名和密码发送到后端服务器。
- 接下来,后端服务器使用Shiro进行身份验证,验证用户的用户名和密码是否正确。
- 如果身份验证成功,Shiro会生成一个唯一的令牌(Token)。
- 最后,后端服务器将该令牌返回给前端,前端可以将其存储在Cookie或LocalStorage中,用于后续的请求验证和授权。
2. 如何在Shiro中生成令牌(Token)并返回给前端?
在Shiro中生成令牌并返回给前端可以通过以下步骤完成:
- 首先,在Shiro的登录认证过程中,验证用户名和密码的正确性。
- 然后,如果验证成功,可以使用Shiro提供的
UsernamePasswordToken
类创建一个令牌对象。 - 接下来,可以将令牌对象转换为字符串形式,以便于在返回给前端时进行传输。
- 最后,将生成的令牌字符串返回给前端,前端可以根据需要进行存储和使用。
3. 如何在前端接收并处理Shiro返回的令牌(Token)?
在前端接收和处理Shiro返回的令牌可以通过以下步骤完成:
- 首先,前端发送登录请求到后端服务器,并等待服务器返回令牌。
- 然后,前端通过适当的方式接收到令牌字符串,例如从服务器响应中获取或从Cookie或LocalStorage中读取。
- 接下来,前端可以将令牌字符串存储在Cookie或LocalStorage中,以便于后续的请求验证和授权。
- 最后,前端可以根据需要使用令牌进行身份验证和授权操作,例如在每次请求头中携带令牌进行访问控制。
原创文章,作者:Edit2,如若转载,请注明出处:https://docs.pingcode.com/baike/2222117