Commit 4a90e740 authored by licc's avatar licc

Merge remote-tracking branch 'origin/master'

parents 235307d2 71d94aa2
package cn.wisenergy.web.shiro;
import org.apache.shiro.authc.AuthenticationToken;
/**
......
package cn.wisenergy.web.shiro;
import cn.wisenergy.web.shiro.filter.AuthFilter;
import cn.wisenergy.web.shiro.filter.AuthRealm;
import cn.wisenergy.web.shiro.filter.JwtFilter;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
......@@ -10,6 +12,7 @@ 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 javax.servlet.Filter;
import java.util.HashMap;
......@@ -18,6 +21,7 @@ import java.util.Map;
/**
* shiro配置类
*
* @author 86187
*/
@Configuration
......@@ -48,7 +52,7 @@ public class ShiroConfig {
// 自定义的过滤器
Map<String, Filter> filterMap = new HashMap<>();
// map里面key值要为过滤器的名称,value为过滤器对象
filterMap.put("oauth2", new AuthFilter());
filterMap.put("oauth2", new JwtFilter());
// 将自定义的过滤器加入到过滤器集合中
shiroFilterFactoryBean.setFilters(filterMap);
......@@ -85,34 +89,44 @@ public class ShiroConfig {
return new LifecycleBeanPostProcessor();
}
@Bean
public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setUsePrefix(true);
return defaultAdvisorAutoProxyCreator;
@Bean(name = "authRealm")
@DependsOn("lifecycleBeanPostProcessor")
public AuthRealm authRealm() {
return new AuthRealm();
}
/**
* 配置加密匹配,使用MD5的方式,进行1024次加密
* securityManager 不用直接注入shiroDBRealm,可能会导致事务失效
* 解决方法见 handleContextRefresh
* http://www.debugrun.com/a/NKS9EJQ.html
*/
// @Bean
// public HashedCredentialsMatcher hashedCredentialsMatcher() {
// HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// hashedCredentialsMatcher.setHashAlgorithmName("MD5");
// hashedCredentialsMatcher.setHashIterations(1024);
// return hashedCredentialsMatcher;
// }
@Bean("securityManager")
public SecurityManager securityManager(AuthRealm authRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(authRealm);
securityManager.setRememberMeManager(null);
/*
* 关闭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);
return securityManager;
}
/**
* 开启Shiro的注解
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public static DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setUsePrefix(true);
// 强制使用cglib,防止重复代理和可能引起代理出错的问题
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
......
package cn.wisenergy.web.shiro.filter;
import cn.wisenergy.common.enums.RespCodeEnum;
import cn.wisenergy.common.utils.HttpContextUtils;
import cn.wisenergy.common.utils.exception.Result;
import cn.wisenergy.web.shiro.AuthToken;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
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;
import java.io.IOException;
/**
* oauth2过滤器
*
* @author 86187
*/
@Slf4j
public class AuthFilter extends AuthenticatingFilter {
/**
* 这里重写了父类的方法,使用我们自己定义的Token类,提交给shiro。
* 这个方法返回null的话会直接抛出异常,进入isAccessAllowed() 的异常处理逻辑。
*
* @throws Exception
*/
@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
//获取请求token
String token = getRequestToken((HttpServletRequest) request);
if (StringUtils.isBlank(token)) {
return null;
}
return new AuthToken(token);
}
/**
* 父类会在请求进入拦截器后调用该方法,返回true则继续,返回false则会调用onAccessDenied()。这里在不通过时,还调用了isPermissive()方法
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) {
return true;
}
// boolean allowed = false;
// try {
// allowed = executeLogin(request, response);
// } catch(IllegalStateException e){ //not found any token
// log.error("Not found any token");
// }catch (Exception e) {
// log.error("Error occurs when login", e);
//package cn.wisenergy.web.shiro.filter;
//
//import cn.wisenergy.common.enums.RespCodeEnum;
//import cn.wisenergy.common.utils.HttpContextUtils;
//import cn.wisenergy.common.utils.exception.Result;
//import cn.wisenergy.web.shiro.AuthToken;
//import com.alibaba.fastjson.JSON;
//import lombok.extern.slf4j.Slf4j;
//import org.apache.commons.lang3.StringUtils;
//import org.apache.http.HttpStatus;
//import org.apache.shiro.authc.AuthenticationException;
//import org.apache.shiro.authc.AuthenticationToken;
//import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
//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;
//import java.io.IOException;
//
///**
// * oauth2过滤器
// *
// * @author 86187
// */
//@Slf4j
//public class AuthFilter extends AuthenticatingFilter {
//
// /**
// * 这里重写了父类的方法,使用我们自己定义的Token类,提交给shiro。
// * 这个方法返回null的话会直接抛出异常,进入isAccessAllowed() 的异常处理逻辑。
// *
// * @throws Exception
// */
// @Override
// protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
// //获取请求token
// String token = getRequestToken((HttpServletRequest) request);
// if (StringUtils.isBlank(token)) {
// return null;
// }
// return new AuthToken(token);
// }
// return allowed || super.isPermissive(mappedValue);
return false;
}
/**
* 如果这个Filter在之前isAccessAllowed()方法中返回false,则会进入这个方法。这里直接返回错误的response
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
if (isLoginRequest(request, response) || isEnabled(request, response)) {
return true;
} else {
//获取请求token,如果token不存在,直接返回40101
String token = getRequestToken((HttpServletRequest) request);
if (StringUtils.isBlank(token)) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
httpResponse.setCharacterEncoding("UTF-8");
Result result = new Result();
result.setResult(Result.RESULT_FLG.FAIL.getValue());
result.setErrorCode(RespCodeEnum.NO_AUTH_REQUEST.getCode());
result.setErrorMsg(RespCodeEnum.NO_AUTH_REQUEST.getMsg());
String json = JSON.toJSONString(result);
httpResponse.getWriter().print(json);
return false;
}
}
return executeLogin(request, response);
}
//
// /**
// * 如果Shiro Login认证成功,会进入该方法,等同于用户名密码登录成功,这里还判断了是否要刷新Token
// * 父类会在请求进入拦截器后调用该方法,返回true则继续,返回false则会调用onAccessDenied()。这里在不通过时,还调用了isPermissive()方法
// */
// @Override
// protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
// HttpServletResponse httpResponse = WebUtils.toHttp(response);
// if(token instanceof AuthToken){
// AuthToken jwtToken = (AuthToken)token;
// String newtoken = jwtToken.getPrincipal();
// if(StringUtils.isNotBlank(newtoken)){
// httpResponse.setHeader("x-auth-token", newtoken);
// protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
// if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) {
// return true;
// }
//// boolean allowed = false;
//// try {
//// allowed = executeLogin(request, response);
//// } catch(IllegalStateException e){ //not found any token
//// log.error("Not found any token");
//// }catch (Exception e) {
//// log.error("Error occurs when login", e);
//// }
//
//// return allowed || super.isPermissive(mappedValue);
// return false;
// }
//
// /**
// * 如果这个Filter在之前isAccessAllowed()方法中返回false,则会进入这个方法。这里直接返回错误的response
// */
// @Override
// protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
// if (isLoginRequest(request, response) || isEnabled(request, response)) {
// return true;
// } else {
// //获取请求token,如果token不存在,直接返回40101
// String token = getRequestToken((HttpServletRequest) request);
// if (StringUtils.isBlank(token)) {
// HttpServletResponse httpResponse = (HttpServletResponse) response;
// httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
// httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
// httpResponse.setCharacterEncoding("UTF-8");
//
// Result result = new Result();
// result.setResult(Result.RESULT_FLG.FAIL.getValue());
// result.setErrorCode(RespCodeEnum.NO_AUTH_REQUEST.getCode());
// result.setErrorMsg(RespCodeEnum.NO_AUTH_REQUEST.getMsg());
// String json = JSON.toJSONString(result);
// httpResponse.getWriter().print(json);
// return false;
// }
// }
//
// return executeLogin(request, response);
// }
//
//// /**
//// * 如果Shiro Login认证成功,会进入该方法,等同于用户名密码登录成功,这里还判断了是否要刷新Token
//// */
//// @Override
//// protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
//// HttpServletResponse httpResponse = WebUtils.toHttp(response);
//// if(token instanceof AuthToken){
//// AuthToken jwtToken = (AuthToken)token;
//// String newtoken = jwtToken.getPrincipal();
//// if(StringUtils.isNotBlank(newtoken)){
//// httpResponse.setHeader("x-auth-token", newtoken);
//// }
//// }
//// return true;
//// }
//
// /**
// * 如果调用shiro的login认证失败,会回调这个方法
// */
// @Override
// protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
// HttpServletResponse httpResponse = (HttpServletResponse) response;
// httpResponse.setContentType("application/json;charset=utf-8");
// httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
// httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
// try {
// //处理登录失败的异常
// Throwable throwable = e.getCause() == null ? e : e.getCause();
// Result result = new Result();
// result.setErrorCode(String.valueOf(HttpStatus.SC_UNAUTHORIZED));
// result.setResult(Result.RESULT_FLG.FAIL.getValue());
// result.setErrorMsg(throwable.getMessage());
// String json = JSON.toJSONString(result);
// httpResponse.getWriter().print(json);
// } catch (IOException e1) {
// }
// return false;
// }
//
// /**
// * 获取请求头中的token
// */
// private String getRequestToken(HttpServletRequest httpRequest) {
// //从header中获取token
// String token = httpRequest.getHeader("Authorization");
// return token;
// }
/**
* 如果调用shiro的login认证失败,会回调这个方法
*/
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setContentType("application/json;charset=utf-8");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
try {
//处理登录失败的异常
Throwable throwable = e.getCause() == null ? e : e.getCause();
Result result = new Result();
result.setErrorCode(String.valueOf(HttpStatus.SC_UNAUTHORIZED));
result.setResult(Result.RESULT_FLG.FAIL.getValue());
result.setErrorMsg(throwable.getMessage());
String json = JSON.toJSONString(result);
httpResponse.getWriter().print(json);
} catch (IOException e1) {
}
return false;
}
/**
* 获取请求头中的token
*/
private String getRequestToken(HttpServletRequest httpRequest) {
//从header中获取token
String token = httpRequest.getHeader("Authorization");
return token;
}
}
//
//}
package cn.wisenergy.web.shiro.filter;
import cn.wisenergy.common.enums.RespCodeEnum;
import cn.wisenergy.common.utils.HttpContextUtils;
import cn.wisenergy.common.utils.exception.Result;
import cn.wisenergy.web.shiro.AuthToken;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 执行流程preHandle->isAccessAllowed->isLoginAttempt->executeLogin->onLoginSuccess
*/
public class JwtFilter extends BasicHttpAuthenticationFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtFilter.class);
/**
* 最先执行的方法
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
return super.preHandle(request, response);
}
/**
* 这里重写了父类的方法,使用我们自己定义的Token类,提交给shiro。
* 这个方法返回null的话会直接抛出异常,进入isAccessAllowed() 的异常处理逻辑。
*/
@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
//获取请求token
String token = ((HttpServletRequest) request).getHeader("Authorization");
if (StringUtils.isBlank(token)) {
return null;
}
return new AuthToken(token);
}
/**
* 这里我们详细说明下为什么最终返回的都是true,即允许访问
* 例如我们提供一个地址 GET /article
* 登入用户和游客看到的内容是不同的
* 如果在这里返回了false,请求会被直接拦截,用户看不到任何东西
* 所以我们在这里返回true,Controller中可以通过 subject.isAuthenticated() 来判断用户是否登入
* 如果有些资源只有登入用户才能访问,我们只需要在方法上面加上 @RequiresAuthentication 注解即可
* 但是这样做有一个缺点,就是不能够对GET,POST等请求进行分别过滤鉴权(因为我们重写了官方的方法),但实际上对应用影响不大
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (isLoginAttempt(request, response)) {
//if (!isLoginAttempt(request, response) || !executeLogin(request,response)) {
try {
executeLogin(request, response);
} catch (UnauthorizedException | AuthenticationException e) {
return false;
}
}
return true;
}
/**
* 判断用户是否想要登入。
* 检测header里面是否包含Authorization字段即可
*/
@Override
protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest) request;
String authorization = req.getHeader("Authorization");
return authorization != null;
}
/**
*
*/
@Override
protected boolean executeLogin(ServletRequest servletRequest, ServletResponse servletResponse) {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String authorization = getRequestToken(request);
if (StringUtils.isNotBlank(authorization)) {
if (authorization.startsWith("Bearer ")) {
authorization = authorization.substring(7);
}
}
AuthToken token = new AuthToken(authorization);
// 提交给realm进行登入,如果错误他会抛出异常并被捕获
getSubject(servletRequest, servletResponse).login(token);
// 如果没有抛出异常则代表登入成功,返回true
return true;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
if (isLoginRequest(request, response)) {
return true;
} else {
//获取请求token,如果token不存在,直接返回40101
String token = getRequestToken((HttpServletRequest) request);
if (StringUtils.isBlank(token)) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
//这里是个坑,如果不设置的接受的访问源,那么前端都会报跨域错误,因为这里还没到corsConfig里面
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
httpResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.setContentType("application/json;charset=UTF-8");
httpResponse.setStatus(401);
Result result = new Result();
result.setResult(Result.RESULT_FLG.FAIL.getValue());
result.setErrorCode(RespCodeEnum.NO_AUTH_REQUEST.getCode());
result.setErrorMsg(RespCodeEnum.NO_AUTH_REQUEST.getMsg());
String json = JSON.toJSONString(result);
httpResponse.getWriter().print(json);
return false;
}
}
return executeLogin(request, response);
}
/**
* 将非法请求返回401
*/
private void response401(ServletRequest req, ServletResponse resp) {
try {
HttpServletResponse response = (HttpServletResponse) resp;
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=utf-8");
response.setStatus(401);
response.getWriter().write("{\"status\":401,\"message\":\"未登录!\"}");
} catch (IOException e) {
LOGGER.error(e.getMessage());
}
}
/**
* 将非法请求返回401
*/
private void response403(ServletRequest req, ServletResponse resp) {
try {
HttpServletResponse response = (HttpServletResponse) resp;
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=utf-8");
response.setStatus(403);
response.getWriter().write("{\"status\":403,\"message\":Unauthorized!}");
} catch (IOException e) {
LOGGER.error(e.getMessage());
}
}
/**
* 获取请求头中的token
*/
private String getRequestToken(HttpServletRequest httpRequest) {
//从header中获取token
return httpRequest.getHeader("Authorization");
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment