自行添加 jwt与shiro依赖
1.jwt工具包
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import tech.liveeasy.biz.camphor.config.SystemConfigProperty;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
@Component
public class JWTUtil {
@Autowired
private SystemConfigProperty systemConfigProperty;
/**
* 获取登陆名
*
* @param token
* @return
*/
public String getLoginName(String token) {
Claims claims = Jwts
.parser()
.setSigningKey(systemConfigProperty.getJwtAppKey())//自定义key
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
/**
* 获取token参数
*
* @param token
* @param key
* @return
*/
public String getclaimsValue(String token, String key) {
Claims claims = Jwts
.parser()
.setSigningKey(systemConfigProperty.getJwtAppKey())//自定义key
.parseClaimsJws(token)
.getBody();
return claims.get(key, String.class);
}
/**
* 获取token参数
*
* @param token
* @param key
* @param clazz
* @param <T>
* @return
*/
public <T> T getclaimsValue(String token, String key, Class<T> clazz) {
Claims claims = Jwts
.parser()
.setSigningKey(systemConfigProperty.getJwtAppKey())//自定义key
.parseClaimsJws(token)
.getBody();
return claims.get(key, clazz);
}
/**
* 验证token是否正确
*
* @param token
* @param username
* @param secret
* @return
*/
public boolean verify(String token, String username, String secret) {
Claims claims = Jwts
.parser()
.setSigningKey(systemConfigProperty.getJwtAppKey())//自定义key
.parseClaimsJws(token)
.getBody();
String subject = claims.getSubject();
String secret1 = claims.get("secret", String.class);
return username.equals(subject) && secret1.equals(secret);
}
/**
* 创建toKen
*
* @param username
* @param password
* @return
*/
public String createToken(String username, String password, Map<String, Object> parameters) {
Calendar curr = Calendar.getInstance();
curr.set(Calendar.DAY_OF_YEAR, curr.get(Calendar.DAY_OF_YEAR) + systemConfigProperty.getJwtAppTokenTimeout());//自定义有效天数
if (null == parameters) {
parameters = new HashMap<>(16);
}
parameters.put("sub", username);
parameters.put("secret", password);
JwtBuilder jwtBuilder = Jwts
.builder()
.setSubject(username)
.setClaims(parameters)
.setExpiration(curr.getTime())
.signWith(SignatureAlgorithm.HS256, systemConfigProperty.getJwtAppKey());//自定义key
return jwtBuilder.compact();
}
/**
* 创建toKen
*
* @param username
* @param password
* @return
*/
public String createToken(String username, String password) {
return createToken(username, password, null);
}
}
2.shiro token修改
import org.apache.shiro.authc.AuthenticationToken;
public class SystemToken implements AuthenticationToken {
private String token;
public SystemToken(String token) {
this.token = token;
}
@Override
public Object getPrincipal() {
return token;
}
@Override
public Object getCredentials() {
return token;
}
}
3.自定义异常
import org.apache.shiro.authc.AuthenticationException;
public class UnauthorizedException extends AuthenticationException {
private int errorCode;
public int getErrorCode() {
return errorCode;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public UnauthorizedException(int errorCode , String msg ) {
super(msg);
this.errorCode = errorCode;
}
public UnauthorizedException(String msg) {
super(msg);
}
public UnauthorizedException() {
super();
}
}
4.自定义SystemRealm
import io.jsonwebtoken.ExpiredJwtException;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.*;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import tech.liveeasy.biz.camphor.apps.user.entity.UserEntity;
import tech.liveeasy.biz.camphor.system.excption.ExceptionCode;
import tech.liveeasy.biz.camphor.system.excption.UnauthorizedException;
import tech.liveeasy.biz.camphor.system.utils.JWTUtil;
import tech.liveeasy.biz.camphor.system.utils.UserUtil;
import java.util.HashSet;
import java.util.Set;
@Component
public class SystemRealm extends AuthorizingRealm {
@Autowired
private JWTUtil jwtUtil;
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof SystemToken;
}
/**
* //角色权限和对应权限添加 通过subject.isPermited()等方法调用
* 只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = jwtUtil.getLoginName(principals.toString());
UserEntity user = UserUtil.getUserByLoginName(username);
Set<String> set = new HashSet<>();
set.add(user.getRules());
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setRoles(set);
simpleAuthorizationInfo.addStringPermissions(user.getPermissionList());
return simpleAuthorizationInfo;
}
/**
* //用户认证 通过subject.login()等方法来调用
* 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
String token = (String) auth.getCredentials();
String username = "";
try {
username = jwtUtil.getLoginName(token);
} catch (ExpiredJwtException expiredJwtException) {
throw new UnauthorizedException(ExceptionCode.TOKEN_EXPIRED.getCode(), ExceptionCode.TOKEN_EXPIRED.getName());
}
if (StringUtils.isEmpty(username)) {
throw new UnauthorizedException(ExceptionCode.UNKNOWN_ACCOUNT.getCode(), ExceptionCode.UNKNOWN_ACCOUNT.getName());
}
String password = UserUtil.getPassword(username);
String status = UserUtil.getStatus(username);
if (!jwtUtil.verify(token, username, password)) {
throw new UnauthorizedException(ExceptionCode.AUTHENTICATION.getCode(), ExceptionCode.AUTHENTICATION.getName());
}
if (!"在职".equals(status)) {
throw new UnauthorizedException(ExceptionCode.DISABLED_ACCOUNT.getCode(), ExceptionCode.DISABLED_ACCOUNT.getName());
}
return new SimpleAuthenticationInfo(token, token, "system_realm");
}
}
5.设置filter
import com.alibaba.fastjson.JSON;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import tech.liveeasy.biz.camphor.apps.common.RestfulResult;
import tech.liveeasy.biz.camphor.system.excption.ExceptionCode;
import tech.liveeasy.biz.camphor.system.excption.UnauthorizedException;
import tech.liveeasy.biz.camphor.system.security.SystemToken;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class SystemFilter extends BasicHttpAuthenticationFilter {
private Logger LOGGER = LoggerFactory.getLogger(this.getClass());
private String rootPath;
public SystemFilter(String rootPath) {
this.rootPath = rootPath;
}
/**
* 对跨域提供支持
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
setHeader(httpServletRequest, httpServletResponse);
return false;
}
return super.preHandle(request, response);
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws UnauthorizedException {
if (isLoginAttempt(request, response)) {
try {
executeLogin(request, response);
return true;
} catch (Exception e) {
throw (RuntimeException) e;
}
} else {
//如果请求头不存在 Authorization,则可能是执行不合规操作直接返回 false 进入onAccessDenied
return false;
}
}
/**
* 处理未经验证的请求
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
throw new UnauthorizedException(ExceptionCode.BLANK_TOKEN.getCode(), ExceptionCode.BLANK_TOKEN.getName());
/* boolean loggedIn = false;
if (this.isLoginAttempt(request, response)) {
loggedIn = this.executeLogin(request, response);
}
if (!loggedIn) {
this.sendChallenge(request, response);
}
return loggedIn;*/
}
/**
* 构建未授权的请求返回,filter层的异常不受exceptionAdvice控制,这里返回401,把返回的json丢到response中
*/
/*@Override
protected boolean sendChallenge(ServletRequest request, ServletResponse response) {
HttpServletResponse httpResponse = WebUtils.toHttp(response);
String contentType = "application/json;charset=UTF-8";
httpResponse.setStatus(401);
httpResponse.setContentType(contentType);
try {
String msg = "对不起,您无权限进行操作!";
RestfulResult unauthentication = new RestfulResult();
unauthentication.setCode("401");
unauthentication.setMsg(msg);
PrintWriter printWriter = httpResponse.getWriter();
printWriter.append(JSON.toJSONString(unauthentication));
} catch (IOException e) {
LOGGER.error("sendChallenge error,can not resolve httpServletResponse");
}
return false;
}*/
/**
* 判断用户是否想要登入
* 检测header里面是否包含Authorization字段即可
*/
@Override
protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest) request;
String authorization = req.getHeader("Authorization");
return authorization != null && !authorization.equals("");
}
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authorization = httpServletRequest.getHeader("Authorization");
SystemToken token = new SystemToken(authorization);
getSubject(request, response).login(token);
// 如果没有抛出异常则代表登入成功,返回true
return true;
}
@Override
protected void cleanup(ServletRequest request, ServletResponse response, Exception existing) throws ServletException, IOException {
if (null == existing) {
super.cleanup(request, response, existing);
} else {
response401(response, existing);
}
}
private void response401(ServletResponse response, Exception throwable) {
try {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
String url = rootPath + "/user";
if (throwable instanceof UnauthorizedException) {
UnauthorizedException exception = (UnauthorizedException) throwable;
url += "/401?errorCode=" + exception.getErrorCode() + "&msg=" + exception.getMessage();
} else {
url += "/401";
}
httpServletResponse.sendRedirect(url);
} catch (IOException e) {
LOGGER.error("response401:", e);
}
}
@Override
protected boolean isRememberMe(ServletRequest request) {
return super.isRememberMe(request);
}
/**
* 为response设置header,实现跨域
*/
private void setHeader(HttpServletRequest request, HttpServletResponse response) {
//跨域的header设置
response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
//防止乱码,适用于传输JSON数据
response.setHeader("Content-Type", "application/json;charset=UTF-8");
response.setStatus(HttpStatus.OK.value());
}
}
最后配置shiro
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.redis.core.StringRedisTemplate;
import tech.liveeasy.biz.camphor.system.filter.CaptchaValidateFilter;
import tech.liveeasy.biz.camphor.system.filter.PhoneCaptchaFilter;
import tech.liveeasy.biz.camphor.system.filter.SystemFilter;
import tech.liveeasy.biz.camphor.system.filter.SystemLogoutFilter;
import tech.liveeasy.biz.camphor.system.security.SystemRealm;
import tech.liveeasy.platform.common.config.Global;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfiguration {
@Bean("securityManager")
public DefaultWebSecurityManager getManager(SystemRealm systemRealm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(systemRealm);
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
manager.setSubjectDAO(subjectDAO);
return manager;
}
@Bean("shiroFilter")
public ShiroFilterFactoryBean factory(DefaultWebSecurityManager securityManager,
SystemConfigProperty systemConfigProperty) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// 添加自定义规则的过滤器
Map<String, Filter> filterMap = new HashMap<>();
//接口权限验证
filterMap.put("system", new SystemFilter(systemConfigProperty.getRootPath()));
factoryBean.setFilters(filterMap);
factoryBean.setSecurityManager(securityManager);
factoryBean.setUnauthorizedUrl(systemConfigProperty.getRootPath() + "/401");
Map<String, String> filterRuleMap = new HashMap<>();
filterRuleMap.put("/401", "anon");
filterRuleMap.put("/user/login", "anon");
filterRuleMap.put("/**", "system");
factoryBean.setFilterChainDefinitionMap(filterRuleMap);
return factoryBean;
}
/**
* 下面的代码是添加注解支持
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
// 强制使用cglib,防止重复代理和可能引起代理出错的问题
// https://zhuanlan.zhihu.com/p/29161098
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}