Commit 4a90e740 authored by licc's avatar licc

Merge remote-tracking branch 'origin/master'

parents 235307d2 71d94aa2
package cn.wisenergy.web.shiro; package cn.wisenergy.web.shiro;
import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.AuthenticationToken;
/** /**
......
package cn.wisenergy.web.shiro; 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.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.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
...@@ -10,6 +12,7 @@ import org.apache.shiro.web.mgt.DefaultWebSecurityManager; ...@@ -10,6 +12,7 @@ import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import javax.servlet.Filter; import javax.servlet.Filter;
import java.util.HashMap; import java.util.HashMap;
...@@ -18,6 +21,7 @@ import java.util.Map; ...@@ -18,6 +21,7 @@ import java.util.Map;
/** /**
* shiro配置类 * shiro配置类
*
* @author 86187 * @author 86187
*/ */
@Configuration @Configuration
...@@ -48,7 +52,7 @@ public class ShiroConfig { ...@@ -48,7 +52,7 @@ public class ShiroConfig {
// 自定义的过滤器 // 自定义的过滤器
Map<String, Filter> filterMap = new HashMap<>(); Map<String, Filter> filterMap = new HashMap<>();
// map里面key值要为过滤器的名称,value为过滤器对象 // map里面key值要为过滤器的名称,value为过滤器对象
filterMap.put("oauth2", new AuthFilter()); filterMap.put("oauth2", new JwtFilter());
// 将自定义的过滤器加入到过滤器集合中 // 将自定义的过滤器加入到过滤器集合中
shiroFilterFactoryBean.setFilters(filterMap); shiroFilterFactoryBean.setFilters(filterMap);
...@@ -73,7 +77,7 @@ public class ShiroConfig { ...@@ -73,7 +77,7 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/**", "oauth2"); // 其他路径均需要身份认证,一般位于最下面,优先级最低 filterChainDefinitionMap.put("/**", "oauth2"); // 其他路径均需要身份认证,一般位于最下面,优先级最低
// 设置拦截器 // 设置拦截器
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean; return shiroFilterFactoryBean;
} }
...@@ -85,34 +89,44 @@ public class ShiroConfig { ...@@ -85,34 +89,44 @@ public class ShiroConfig {
return new LifecycleBeanPostProcessor(); return new LifecycleBeanPostProcessor();
} }
@Bean @Bean(name = "authRealm")
public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() { @DependsOn("lifecycleBeanPostProcessor")
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); public AuthRealm authRealm() {
defaultAdvisorAutoProxyCreator.setUsePrefix(true); return new AuthRealm();
return defaultAdvisorAutoProxyCreator;
} }
/** /**
* 配置加密匹配,使用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") @Bean("securityManager")
public SecurityManager securityManager(AuthRealm authRealm) { public SecurityManager securityManager(AuthRealm authRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(authRealm); securityManager.setRealm(authRealm);
securityManager.setRememberMeManager(null); 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; return securityManager;
} }
/** @Bean
* 开启Shiro的注解 @DependsOn("lifecycleBeanPostProcessor")
*/ public static DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setUsePrefix(true);
// 强制使用cglib,防止重复代理和可能引起代理出错的问题
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
@Bean @Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
......
package cn.wisenergy.web.shiro.filter; //package cn.wisenergy.web.shiro.filter;
//
import cn.wisenergy.common.enums.RespCodeEnum; //import cn.wisenergy.common.enums.RespCodeEnum;
import cn.wisenergy.common.utils.HttpContextUtils; //import cn.wisenergy.common.utils.HttpContextUtils;
import cn.wisenergy.common.utils.exception.Result; //import cn.wisenergy.common.utils.exception.Result;
import cn.wisenergy.web.shiro.AuthToken; //import cn.wisenergy.web.shiro.AuthToken;
import com.alibaba.fastjson.JSON; //import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j; //import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; //import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus; //import org.apache.http.HttpStatus;
import org.apache.shiro.authc.AuthenticationException; //import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken; //import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter; //import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.web.bind.annotation.RequestMethod; //import org.springframework.web.bind.annotation.RequestMethod;
//
import javax.servlet.ServletRequest; //import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; //import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; //import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; //import javax.servlet.http.HttpServletResponse;
import java.io.IOException; //import java.io.IOException;
//
/** ///**
* oauth2过滤器 // * oauth2过滤器
* // *
* @author 86187 // * @author 86187
*/ // */
@Slf4j //@Slf4j
public class AuthFilter extends AuthenticatingFilter { //public class AuthFilter extends AuthenticatingFilter {
//
/** // /**
* 这里重写了父类的方法,使用我们自己定义的Token类,提交给shiro。 // * 这里重写了父类的方法,使用我们自己定义的Token类,提交给shiro。
* 这个方法返回null的话会直接抛出异常,进入isAccessAllowed() 的异常处理逻辑。 // * 这个方法返回null的话会直接抛出异常,进入isAccessAllowed() 的异常处理逻辑。
* // *
* @throws Exception // * @throws Exception
*/ // */
@Override // @Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception { // protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
//获取请求token // //获取请求token
String token = getRequestToken((HttpServletRequest) request); // String token = getRequestToken((HttpServletRequest) request);
if (StringUtils.isBlank(token)) { // if (StringUtils.isBlank(token)) {
return null; // return null;
} // }
return new AuthToken(token); // return new AuthToken(token);
} // }
//
/** // /**
* 父类会在请求进入拦截器后调用该方法,返回true则继续,返回false则会调用onAccessDenied()。这里在不通过时,还调用了isPermissive()方法 // * 父类会在请求进入拦截器后调用该方法,返回true则继续,返回false则会调用onAccessDenied()。这里在不通过时,还调用了isPermissive()方法
*/ // */
@Override // @Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { // protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) { // if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) {
return true; // 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);
// } // }
//// boolean allowed = false;
// return allowed || super.isPermissive(mappedValue); //// try {
return false; //// allowed = executeLogin(request, response);
} //// } catch(IllegalStateException e){ //not found any token
//// log.error("Not found any token");
/** //// }catch (Exception e) {
* 如果这个Filter在之前isAccessAllowed()方法中返回false,则会进入这个方法。这里直接返回错误的response //// log.error("Error occurs when login", e);
*/ //// }
@Override //
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { //// return allowed || super.isPermissive(mappedValue);
if (isLoginRequest(request, response) || isEnabled(request, response)) { // return false;
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 // * 如果这个Filter在之前isAccessAllowed()方法中返回false,则会进入这个方法。这里直接返回错误的response
// */ // */
// @Override // @Override
// protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { // protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
// HttpServletResponse httpResponse = WebUtils.toHttp(response); // if (isLoginRequest(request, response) || isEnabled(request, response)) {
// if(token instanceof AuthToken){ // return true;
// AuthToken jwtToken = (AuthToken)token; // } else {
// String newtoken = jwtToken.getPrincipal(); // //获取请求token,如果token不存在,直接返回40101
// if(StringUtils.isNotBlank(newtoken)){ // String token = getRequestToken((HttpServletRequest) request);
// httpResponse.setHeader("x-auth-token", newtoken); // 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 true; //
// 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