spring boot整合shiro之shiro過濾器介紹
阿新 • • 發佈:2019-01-09
過濾器鏈條配置說明
- 1、一個URL可以配置多個Filter,使用逗號分隔
- 2、當設定多個過濾器時,全部驗證通過,才視為通過
- 3、部分過濾器可指定引數,如perms,roles
Shiro內建的FilterChain
- anon(org.apache.shiro.web.filter.authc.AnonymousFilter):例子/admins/**=anon 沒有引數,表示可以匿名使用。
- authc(org.apache.shiro.web.filter.authc.FormAuthenticationFilter):例如/admins/user/**=authc表示需要認證(登入)才能使用,沒有引數
- authcBasic(org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter):例如/admins/user/**=authcBasic,沒有引數表示httpBasic認證
- perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter:(許可權)例子/admins/user/=perms[user:add:],引數可以寫多個,多個時必須加上引號,並且引數之間用逗號分割,例如/admins/user/=perms[“user:add:,user:modify:*”],當有多個引數時必須每個引數都通過才通過,想當於isPermitedAll()方法。
- port org.apache.shiro.web.filter.authz.PortFilter:例子/admins/user/**=port[8081],當請求的url的埠不是8081是跳轉到schemal://serverName:8081?queryString,其中schmal是協議http或https等,serverName是你訪問的host,8081是url配置裡port的埠,queryString
- rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter:例子/admins/user/=rest[user],根據請求的方法,相當於/admins/user/
- roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter:(角色)例子/admins/user/=roles[admin],引數可以寫多個,多個時必須加上引號,並且引數之間用逗號分割,當有多個引數時,例如admins/user/=roles[“admin,guest”],每個引數通過才算通過,相當於hasAllRoles()方法。
- ssl org.apache.shiro.web.filter.authz.SslFilter:例子/admins/user/**=ssl沒有引數,表示安全的url請求,協議為https
- user org.apache.shiro.web.filter.authc.UserFilter:例如/admins/user/**=user沒有引數表示必須存在使用者,當登入操作時不做檢查
配置注意事項
- /admin?=authc 表示可以請求以admin開頭的字串,如xxx/adminfefe,但無法匹配多個,即xxx/admindf/admin是不行的
- /admin*=authc 表示可以匹配零個或者多個字元,如/admin,/admin1,/admin123,但是不能匹配/admin/abc這種
- /admin/**=authc 表示可以匹配零個或者多個路徑,如/admin,/admin/ad/adfdf等
- /login=anon 和 /login=anon/ 不一樣
自定義過濾器介紹
- 過濾器一般實現org.apache.shiro.web.filter.authc.AuthenticatingFilter類。
- 方法使用說明
package cn.xo68.boot.auth.server.shiro.filter;
import java.io.IOException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import cn.xo68.boot.auth.core.domain.OAuth2AuthenticationToken;
import cn.xo68.boot.auth.core.domain.Oauth2Principal;
import cn.xo68.boot.auth.core.properties.OAuthResourceProperties;
import cn.xo68.boot.auth.server.properties.AuthServerProperties;
import cn.xo68.core.util.StringTools;
import org.apache.oltu.oauth2.client.OAuthClient;
import org.apache.oltu.oauth2.client.URLConnectionClient;
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
import org.apache.oltu.oauth2.client.response.OAuthAccessTokenResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.apache.oltu.oauth2.common.message.types.ParameterStyle;
import org.apache.oltu.oauth2.rs.request.OAuthAccessResourceRequest;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.subject.WebSubject;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
/**
* oauth2 認證過濾器
* @author wuxie
* @date 2018-8-5
*/
public class OAuth2AuthenticationFilter extends AuthenticatingFilter {
private static final Logger logger= LoggerFactory.getLogger(OAuth2AuthenticationFilter.class);
private SimpleCookie accessTokenCookie;
private AuthServerProperties authServerProperties;
private OAuthResourceProperties oAuthResourceProperties;
//oauth2 authc code引數名
private String authcCodeParam = "code";
//客戶端id
private String clientId;
//伺服器端登入成功/失敗後重定向到的客戶端地址
private String redirectUrl;
//oauth2伺服器響應型別
private String responseType = "code";
private String failureUrl;
public SimpleCookie getAccessTokenCookie() {
return accessTokenCookie;
}
public void setAccessTokenCookie(SimpleCookie accessTokenCookie) {
this.accessTokenCookie = accessTokenCookie;
}
public AuthServerProperties getAuthServerProperties() {
return authServerProperties;
}
public void setAuthServerProperties(AuthServerProperties authServerProperties) {
this.authServerProperties = authServerProperties;
}
public OAuthResourceProperties getoAuthResourceProperties() {
return oAuthResourceProperties;
}
public void setoAuthResourceProperties(OAuthResourceProperties oAuthResourceProperties) {
this.oAuthResourceProperties = oAuthResourceProperties;
}
public void setAuthcCodeParam(String authcCodeParam) {
this.authcCodeParam = authcCodeParam;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public void setRedirectUrl(String redirectUrl) {
this.redirectUrl = redirectUrl;
}
public void setResponseType(String responseType) {
this.responseType = responseType;
}
public void setFailureUrl(String failureUrl) {
this.failureUrl = failureUrl;
}
/**
* 如命名字面意思,根據請求,生成一個令牌。OAuth2AuthenticationToken是我自定義的
*/
@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpRequest = (HttpServletRequest) request;
try{
OAuthAccessResourceRequest oauthRequest = new OAuthAccessResourceRequest(httpRequest, ParameterStyle.HEADER, ParameterStyle.QUERY);
OAuth2AuthenticationToken oAuth2AuthenticationToken=new OAuth2AuthenticationToken();
Oauth2Principal oauth2Principal=new Oauth2Principal();
oAuth2AuthenticationToken.setPrincipal(oauth2Principal);
//令牌
String accessToken = oauthRequest.getAccessToken();
if(StringTools.isEmpty(accessToken)){
accessToken=accessTokenCookie.getValue();
}
if(StringTools.isNotEmpty(accessToken)){
oAuth2AuthenticationToken.setCredential(accessToken);
oauth2Principal.setAccessToken(accessToken);
return oAuth2AuthenticationToken;
}else {
//authorize_code
String code = httpRequest.getParameter(authcCodeParam);
if(StringTools.isNotEmpty(code)){
//換令牌
//accessToken="";
//oAuth2AuthenticationToken.setAccessToken(accessToken);
//return oAuth2AuthenticationToken;
OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
OAuthClientRequest accessTokenRequest = OAuthClientRequest
.tokenLocation(oAuthResourceProperties.getAccessTokenUrl())
.setGrantType(GrantType.AUTHORIZATION_CODE)
.setClientId(oAuthResourceProperties.getClientId())
.setClientSecret(oAuthResourceProperties.getClientSecret())
.setCode(code)
.setRedirectURI(redirectUrl)
.buildQueryMessage();
OAuthAccessTokenResponse oAuthResponse = oAuthClient.accessToken(accessTokenRequest, OAuth.HttpMethod.POST);
accessToken=oAuthResponse.getAccessToken();
oAuth2AuthenticationToken.setCredential(accessToken);
oauth2Principal.setAccessToken(accessToken);
return oAuth2AuthenticationToken;
}
}
}catch (OAuthProblemException e){
logger.warn("過濾器中獲取令牌令牌異常", e);
}
return new OAuth2AuthenticationToken();
}
/**
* 根據請求資訊,引數等資訊判斷是否允許通過,如果返回false,則是不通過。最終是否去訪問web處理,有isAccessAllowed,onAccessDenied方法共同或運算決定,也就是隻要有一個是true就會訪問web控制器或action。
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
return false;
}
/**
*根據請求,拒絕通過處理,如果返回false,則不再去訪問web控制器或action
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
String error = request.getParameter("error");
String errorDescription = request.getParameter("error_description");
if(!StringUtils.isEmpty(error)) {//如果服務端返回了錯誤,也就是服務端裡檢查不通過進入if裡返回的錯誤
WebUtils.issueRedirect(request, response, authServerProperties.getUnauthorizedUrl() + "?error=" + error + "error_description=" + errorDescription);
return false;
}
// Subject subject = getSubject(request, response);
// if(!subject.isAuthenticated()) {
// if(StringUtils.isEmpty(request.getParameter(authcCodeParam))) {
// //如果使用者沒有身份驗證,且沒有auth code,則重定向到服務端授權,即訪問AuthorizeController的authorize方法
// saveRequestAndRedirectToLogin(request, response);
// return false;
// }
// }
return executeLogin(request, response);
}
/**
* 登入驗證處理,父類本來就有,只是有個bug,按官方給出的方法進行了重寫
*/
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
AuthenticationToken token = this.createToken(request, response);
if (token == null) {
String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken must be created in order to execute a login attempt.";
throw new IllegalStateException(msg);
} else {
try {
//修復bug程式碼,也算個不小的坑吧
Subject subject = new WebSubject.Builder(request, response).buildSubject();
subject.login(token);
ThreadContext.bind(subject);
return this.onLoginSuccess(token, subject, request, response);
} catch (AuthenticationException var5) {
return this.onLoginFailure(token, var5, request, response);
}
}
}
@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,
ServletResponse response) throws Exception {
//issueSuccessRedirect(request, response);
Subject msubject=subject;
return true;
}
/**
*登入失敗處理(認證令牌驗證失敗)
*/
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException ae, ServletRequest request,
ServletResponse response) {
Subject subject = getSubject(request, response);
if (subject.isAuthenticated() || subject.isRemembered()) {
try {
issueSuccessRedirect(request, response);
} catch (Exception e) {
e.printStackTrace();
}
} else {
try {
WebUtils.issueRedirect(request, response, authServerProperties.getUnauthorizedUrl());
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
}