拦截器
1.结构架构图
2.Shiro拦截器
2.1.ShiroConfig
代码:
package com.auth;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Autowired
private LoginRealm loginRealm;
/**
* 配置安全管理器:哪种类型的管理器
* @return
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
/*
* 关闭shiro自带的session,详情见文档
* http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29
*/
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
// 设置自定义 realm.
securityManager.setRealm(loginRealm);
return securityManager;
}
/**
* 配置拦截器
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean factory(SecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(securityManager);
Map filterMap = new HashMap();
factoryBean.setFilters(filterMap);
// 设置无权限时跳转的 url;
Map<String, String> filterRuleMap = new HashMap();
//访问/login和/unauthorized 不需要经过过滤器
//设置我们自定义的JWT过滤器
filterMap.put("jwt", new JWTFilter());
//不登录就能访问的写这里
//filterRuleMap.put("/api2/**", "anon");
filterRuleMap.put("/api/**","jwt,authc");//此网页需要过滤判断以后才能通过
//必须要登录才能访问写这里
filterRuleMap.put("/**", "anon");
// 访问 /unauthorized/** 不通过JWTFilter
factoryBean.setFilterChainDefinitionMap(filterRuleMap);
return factoryBean;
}
}
2.1.1.该类是一个config类(配置类)
2.1.2.设置我们自定义的JWT过滤器,并命名为jwt
filterMap.put("jwt", new JWTFilter());
2.1.3.必须要登录才能访问(要验证令牌,使用"jwt,authc",一般用于删除、修改、查询等界面)
filterRuleMap.put("/api/**","jwt,authc");
2.1.4.不登录就能访问(直接放行,不需要去做验证,一般用于登录,首页等界面)
filterRuleMap.put("/**", "anon");
2.2.JWTFilter
代码:
package com.auth;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class JWTFilter extends BasicHttpAuthenticationFilter {
/**
* 第二步
* 是否能进下一步处理
*
* @param request
* @param response
* @param mappedValue
* @return true可以访问action了,false则执行onAccessDenied
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
try {
//System.out.println("isAccessaLLOWED");
//得到客户端传过来的令牌
String token = ((HttpServletRequest) request).getHeader("token");
//System.out.println("客户端令牌"+token);
//封装
JWTToken jwtToken = new JWTToken(token);
//登录:实际是调用LoginRealm里面doGetAuthenticationInfo
getSubject(request, response).login(jwtToken);
//判断是否有权限
String url = ((HttpServletRequest) request).getRequestURI();
getSubject(request, response).checkPermission(url);
return true;
} catch (Exception e) {
return false;
}
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
System.out.println("onAccessDenied");
HttpServletResponse r = (HttpServletResponse) response;
r.setStatus(401);
return false;
}
/**
* 用于跨域 第一步
*
* @param request
* @param response
* @return true可以进下一步isAccessAllowed,如果false则被拦截不能通过
* @throws Exception
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
//System.out.println("这是preHandle");
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.addHeader("Access-Control-Allow-Origin", "*");
httpServletResponse.addHeader("Access-Control-Allow-Headers", "*");
httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE");
httpServletResponse.addHeader("Access-Control-Expose-Headers", "token");
// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
//更新令牌且发送给客户端
String token = ((HttpServletRequest) request).getHeader("token");
//System.out.println("更新令牌"+token);
String newToken = JwtUtil.updateToken(token);
if (newToken.length() > 0) {
httpServletResponse.addHeader("token", newToken);
//System.out.println("更新以后的令牌判断"+token);
}
return super.preHandle(request, response);
}
}
2.2.1.用于跨域
2.2.2更新令牌且发送给客户端放在header里的原因是,最好在body里放数据。当newToken.length()等于0时,说明令牌失效或者是错误的。
String token = ((HttpServletRequest) request).getHeader("token");
String newToken = JwtUtil.updateToken(token);
if (newToken.length() > 0) {
httpServletResponse.addHeader("token", newToken);
}
return super.preHandle(request, response);
2.3.JWTToken
代码:
package com.auth;
import org.apache.shiro.authc.AuthenticationToken;
public class JWTToken implements AuthenticationToken {
private String token;
public JWTToken(){}
/**
* Instantiates a new Jwt token.
*
* @param token the token
*/
public JWTToken(String token) {
this.token = token;
}
/**
* 以前的用户名
* @return
*/
@Override
public Object getPrincipal() {
return token;
}
/**
* 以前的密码
* @return
*/
@Override
public Object getCredentials() {
return token;
}
}
2.3.1.相当于pojo,里面有用户名、密码
2.4.LoginRealm
代码:
ackage com.auth;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Component;
@Component
public class LoginRealm extends AuthorizingRealm {
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JWTToken;
}
/**
* 管授权
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
String tokenString = principals.toString();
String username = JwtUtil.getUsername(tokenString);
//查数据库,用户的权限,略,通过用户名从数据库查
authorizationInfo.addStringPermission("/api/show");
return authorizationInfo;
}
/**
* 管登录
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//JWTFilter传过来的令牌
String tokenString =(String) token.getPrincipal();
//System.out.println("JWTFilter传过来的令牌"+tokenString);
//取用户名,如果为null则无效
String username = JwtUtil.getUsername(tokenString);
if (username==null)
{
throw new RuntimeException("令牌无效");
}
return new SimpleAuthenticationInfo(tokenString, tokenString, getName());
}
}
2.4.1.主要是两个功能,一个是管理登录,还有一个是管理权限
2.4.2.JWTFilter传过来的令牌
String tokenString =(String) token.getPrincipal();
2.4.3.取用户名,如果为null则无效返回的new SimpleAuthenticationInfo(tokenString, tokenString, getName()),其实是一个布尔类型即true或者false
String username = JwtUtil.getUsername(tokenString);
if (username==null)
{
throw new RuntimeException("令牌无效");
}
return new SimpleAuthenticationInfo(tokenString, tokenString, getName());
2.4.4.查数据库,用户的权限,略
authorizationInfo.addStringPermission("/api/searchDep");authorizationInfo.addStringPermission("/api/removeDep");
2.5.JwtUtil
代码:
package com.auth;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;
@Component
public class JwtUtil {
//密码,绝对保密,写在配置文件中
public static String sercetKey = "mingtianhenganghao";
public final static long keeptime = 180000000;
/* @Value("${token.sercetKey}")
public static String sercetKey;
@Value("${token.keeptime:30000}")
public static long keeptime;*/
public static void main(String[] args) {
//根据用户名产生令牌
// String token = JwtUtil.generToken("admin", null,null);
//System.out.println(token);
//根据令牌取用户名
String token="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJhZG1pbiIsImlhdCI6MTU5OTkxMzA5MSwiZXhwIjoxNTk5OTMxMDkxfQ.9_P_o9hwbsdlo2517jfSUoB3flUvCvOg1m96MCwHkks";
String username = JwtUtil.getUsername(token);
System.out.println(username);
}
/**
* 产生令牌
* @param id 用户名
* @param issuer 签发者
* @param subject 主体(内容)
* @return
*/
public static String generToken(String id, String issuer, String subject) {
long ttlMillis = keeptime;
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(sercetKey);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
JwtBuilder builder = Jwts.builder().setId(id)
.setIssuedAt(now);
if (subject != null) {
builder.setSubject(subject);
}
if (issuer != null) {
builder.setIssuer(issuer);
}
builder.signWith(signatureAlgorithm, signingKey);
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis;
Date exp = new Date(expMillis);
builder.setExpiration(exp);
}
return builder.compact();
}
/**
* 更新令牌
* @param token
* @return
*/
public static String updateToken(String token) {
try {
Claims claims = verifyToken(token);
String id = claims.getId();
String subject = claims.getSubject();
String issuer = claims.getIssuer();
Date date = claims.getExpiration();
return generToken(id, issuer, subject);
} catch (Exception ex) {
// ex.printStackTrace();
}
return "";
}
/**
* 获取用户名
* @param token
* @return
*/
public static String getUsername(String token) {
try {
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(sercetKey))
.parseClaimsJws(token).getBody();
return claims.getId();
} catch (Exception e) {
//e.printStackTrace();
}
return null;
}
public static Claims verifyToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(sercetKey))
.parseClaimsJws(token).getBody();
return claims;
}
}
2.5.1.三个功能:将用户信息变为令牌,将令牌还原,更新令牌(设置令牌失效时间)
2.5.2.产生令牌
public static String generToken(String id, String issuer, String subject) {
long ttlMillis = keeptime;
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(sercetKey);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
JwtBuilder builder = Jwts.builder().setId(id)
.setIssuedAt(now);
if (subject != null) {
builder.setSubject(subject);
}
if (issuer != null) {
builder.setIssuer(issuer);
}
builder.signWith(signatureAlgorithm, signingKey);
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis;
Date exp = new Date(expMillis);
builder.setExpiration(exp);
}
return builder.compact();
}
2.5.3.还原令牌信息(获取用户名)
public static String getUsername(String token) {
try {
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(sercetKey))
.parseClaimsJws(token).getBody();
return claims.getId();
} catch (Exception e) {
//e.printStackTrace();
}
return null;
}
2.5.4.更新令牌(设置令牌实效时间)
public static String updateToken(String token) {
try {
Claims claims = verifyToken(token);
String id = claims.getId();
String subject = claims.getSubject();
String issuer = claims.getIssuer();
Date date = claims.getExpiration();
return generToken(id, issuer, subject);
} catch (Exception ex) {
// ex.printStackTrace();
}
return "";
}
(设置实效时间)
/* @Value("${token.sercetKey}") public static String sercetKey; @Value("${token.keeptime:30000}") public static long
读者福利:
由于篇幅限制,不能完整的把全部内容分享出来,如果你觉得此文档对你有帮助,那就关注+点赞哦