spring boot整合shiro之shiro过滤器介绍

过滤器链条配置说明

  • 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/=perms[user:method] ,其中method为post,get,delete等。
  • 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 不会对http://localhost:8080/login/起效果
  • /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;
    }
}

你可能感兴趣的:(安全,SpringCloud,软件架构)