当前位置 博文首页 > 童话述说我的结局:SpringSecurity应用篇

    童话述说我的结局:SpringSecurity应用篇

    作者:童话述说我的结局 时间:2021-01-17 02:01

    前面吹水原理吹了一篇幅了,现在讲解下应用篇幅,前面说过,如果要用SpringSecurity的话要先导入一个包

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>

    如果要更改默认的帐号密码的话就配置如下

    spring.security.user.name=admin
    spring.security.user.password=admin

    一、修改默认配置

    通过前面的分析知道了修改默认一个是在配置文件中修改,另一个是自定义SpringSecurity配置类,重写配置类方法

    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    
    @Configuration
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
             auth.inMemoryAuthentication()
                    .withUser("admin")
                    .password("{noop}admin")//不加密
                    .authorities("ADMIN");
    
        }
        //自定义过滤器链
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
            //使用默认的过滤器链
            super.configure(httpSecurity);
        }
    
    }

    上面的配置虽然达到了修改帐号密码及权限的目的,但是有一个问题,那就是现在一切都是写死的,而在真实环境中这些数据来源都是数据库,所以如果想要了解怎么从数据库中动态获取用户信息,那就要先从认证的源码进行分析起,有上篇的原码篇说明,可以很清楚的知道认证的过滤器是UsernamePasswordAuthenticationFilter类,从下面源码可以看到默认的表单传递过来帐号密码这里都有接收,这个认证的过滤器他继承了AbstractAuthenticationProcessingFilter这个过滤器

    public class UsernamePasswordAuthenticationFilter extends
            AbstractAuthenticationProcessingFilter {
        // ~ Static fields/initializers
        // =====================================================================================
    
        public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
        public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
    
        private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
        private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
        private boolean postOnly = true;
    
        // ~ Constructors
        // ===================================================================================================
    
        public UsernamePasswordAuthenticationFilter() {
            super(new AntPathRequestMatcher("/login", "POST"));
        }
    
        // ~ Methods
        // ========================================================================================================
    
        public Authentication attemptAuthentication(HttpServletRequest request,
                HttpServletResponse response) throws AuthenticationException {
            if (postOnly && !request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException(
                        "Authentication method not supported: " + request.getMethod());
            }
    
            String username = obtainUsername(request);
            String password = obtainPassword(request);
    
            if (username == null) {
                username = "";
            }
    
            if (password == null) {
                password = "";
            }
    
            username = username.trim();
    
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                    username, password);
    
            // Allow subclasses to set the "details" property
            setDetails(request, authRequest);
    
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    
        /**
         * Enables subclasses to override the composition of the password, such as by
         * including additional values and a separator.
         * <p>
         * This might be used for example if a postcode/zipcode was required in addition to
         * the password. A delimiter such as a pipe (|) should be used to separate the
         * password and extended value(s). The <code>AuthenticationDao</code> will need to
         * generate the expected password in a corresponding manner.
         * </p>
         *
         * @param request so that request attributes can be retrieved
         *
         * @return the password that will be presented in the <code>Authentication</code>
         * request token to the <code>AuthenticationManager</code>
         */
        @Nullable
        protected String obtainPassword(HttpServletRequest request) {
            return request.getParameter(passwordParameter);
        }
    
        /**
         * Enables subclasses to override the composition of the username, such as by
         * including additional values and a separator.
         *
         * @param request so that request attributes can be retrieved
         *
         * @return the username that will be presented in the <code>Authentication</code>
         * request token to the <code>AuthenticationManager</code>
         */
        @Nullable
        protected String obtainUsername(HttpServletRequest request) {
            return request.getParameter(usernameParameter);
        }
    
        /**
         * Provided so that subclasses may configure what is put into the authentication
         * request's details property.
         *
         * @param request that an authentication request is being created for
         * @param authRequest the authentication request object that should have its details
         * set
         */
        protected void setDetails(HttpServletRequest request,
                UsernamePasswordAuthenticationToken authRequest) {
            authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
        }
    
        /**
         * Sets the parameter name which will be used to obtain the username from the login
         * request.
         *
         * @param usernameParameter the parameter name. Defaults to "username".
         */
        public void setUsernameParameter(String usernameParameter) {
            Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
            this.usernameParameter = usernameParameter;
        }
    
        /**
         * Sets the parameter name which will be used to obtain the password from the login
         * request..
         *
         * @param passwordParameter the parameter name. Defaults to "password".
         */
        public void setPasswordParameter(String passwordParameter) {
            Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
            this.passwordParameter = passwordParameter;
        }
    
        /**
         * Defines whether only HTTP POST requests will be allowed by this filter. If set to
         * true, and an authentication request is received which is not a POST request, an
         * exception will be raised immediately and authentication will not be attempted. The
         * <tt>unsuccessfulAuthentication()</tt> method will be called as if handling a failed
         * authentication.
         * <p>
         * Defaults to <tt>true</tt> but may be overridden by subclasses.
         */
        public void setPostOnly(boolean postOnly) {
            this.postOnly = postOnly;
        }
    
        public final String getUsernameParameter() {
            return usernameParameter;
        }
    
        public final String getPasswordParameter() {
            return passwordParameter;
        }
    }

    进入AbstractAuthenticationProcessingFilter过滤器;里面有个doFilter方法,具体的就看这个方法

    public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
            implements ApplicationEventPublisherAware, MessageSourceAware {
        // ~ Static fields/initializers
        // =====================================================================================
    
        // ~ Instance fields
        // ================================================================================================
    
        protected ApplicationEventPublisher eventPublisher;
        protected AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
        private AuthenticationManager authenticationManager;
        protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
        private RememberMeServices rememberMeServices = new NullRememberMeServices();
    
        private RequestMatcher requiresAuthenticationRequestMatcher;
    
        private boolean continueChainBeforeSuccessfulAuthentication = false;
    
        private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
    
        private boolean allowSessionCreation = true;
    
        private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
        private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
    
        // ~ Constructors
        // ===================================================================================================
    
        /**
         * @param defaultFilterProcessesUrl the default value for <tt>filterProcessesUrl</tt>.
         */
        protected AbstractAuthenticationProcessingFilter(String defaultFilterProcessesUrl) {
            setFilterProcessesUrl(defaultFilterProcessesUrl);
        }
    
        /**
         * Creates a new instance
         *
         * @param requiresAuthenticationRequestMatcher the {@link RequestMatcher} used to
         * determine if authentication is required. Cannot be null.
         */
        protected AbstractAuthenticationProcessingFilter(
                RequestMatcher requiresAuthenticationRequestMatcher) {
            Assert.notNull(requiresAuthenticationRequestMatcher,
                    "requiresAuthenticationRequestMatcher cannot be null");
            this.requiresAuthenticationRequestMatcher = requiresAuthenticationRequestMatcher;
        }
    
        // ~ Methods
        // ========================================================================================================
    
        @Override
        public void afterPropertiesSet() {
            Assert.notNull(authenticationManager, "authenticationManager must be specified");
        }
    
        /**
         * Invokes the
         * {@link #requiresAuthentication(HttpServletRequest, HttpServletResponse)
         * requiresAuthentication} method to determine whether the request is for
         * authentication and should be handled by this filter. If it is an authentication
         * request, the
         * {@link #attemptAuthentication(HttpServletRequest, HttpServletResponse)
         * attemptAuthentication} will be invoked to perform the authentication. There are
         * then three possible outcomes:
         * <ol>
         * <li>An <tt>Authentication</tt> object is returned. The configured
         * {@link SessionAuthenticationStrategy} will be invoked (to handle any
         * session-related behaviour such as creating a new session to protect against
         * session-fixation attacks) followed by the invocation of
         * {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, Authentication)}
         * method</li>
         * <li>An <tt>AuthenticationException</tt> occurs during authentication. The
         * {@link #unsuccessfulAuthentication(HttpServletRequest, HttpServletResponse, AuthenticationException)
         * unsuccessfulAuthentication} method will be invoked</li>
         * <li>Null is returned, indicating that the authentication process is incomplete. The
         * method will then return immediately, assuming that the subclass has done any
         * necessary work (such as redirects) to continue the authentication process. The
         * assumption is that a later request will be received by this method where the
         * returned <tt>Authentication</tt> object is not null.
         * </ol>
         */
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
                throws IOException, ServletException {
    
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) res;
    
            if (!requiresAuthentication(request, response)) {
                chain.doFilter(request, response);
    
                return;
            }
    
            if (logger.isDebugEnabled()) {
                logger.debug("Request is to process authentication");
            }
    
            Authentication authResult;
    
            try {
    //attemptAuthentication是过滤认证信息的,这个方法是上层的抽象方法,是交给子类去实现的 authResult
    = attemptAuthentication(request, response); if (authResult == null) { // return immediately as subclass has indicated that it hasn't completed // authentication return; } sessionStrategy.onAuthentication(authResult, request, response); } catch (InternalAuthenticationServiceException failed) { logger.error( "An internal error occurred while trying to authenticate the user.", failed); unsuccessfulAuthentication(request, response, failed); return; } catch (AuthenticationException failed) { // Authentication failed unsuccessfulAuthentication(request, response, failed); return; } // Authentication success if (continueChainBeforeSuccessfulAuthentication) { chain.doFilter(request, response); } successfulAuthentication(request, response, chain, authResult); } /** * Indicates whether this filter should attempt to process a login request for the * current invocation. * <p> * It strips any parameters from the "path" section of the request URL (such as the * jsessionid parameter in <em>https://host/myapp/index.html;jsessionid=blah</em>) * before matching against the <code>filterProcessesUrl</code> property. * <p> * Subclasses may override for special requirements, such as Tapestry integration. * * @return <code>true</code> if the filter should attempt authentication, * <code>false</code> otherwise. */ protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) { return requiresAuthenticationRequestMatcher.matches(request); } /** * Performs actual authentication. * <p> * The implementation should do one of the following: * <ol> * <li>Return a populated authentication token for the authenticated user, indicating * successful authentication</li> * <li>Return null, indicating that the authentication process is still in progress. * Before returning, the implementation should perform any additional work required to * complete the process.</li> * <li>Throw an <tt>AuthenticationException</tt> if the authentication process fails</li> * </ol> * * @param request from which to extract parameters and perform the authentication * @param response the response, which may be needed if the implementation has to do a * redirect as part of a multi-stage authentication process (such as OpenID). * * @return the authenticated user token, or null if authentication is incomplete. * * @throws AuthenticationException if authentication fails. */ public abstract Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException; /** * Default behaviour for successful authentication. * <ol> * <li>Sets the successful <tt>Authentication</tt> object on the * {@link SecurityContextHolder}</li> * <li>Informs the configured <tt>RememberMeServices</tt> of the successful login</li> * <li>Fires an {@link InteractiveAuthenticationSuccessEvent} via the configured * <tt>ApplicationEventPublisher</tt></li> * <li>Delegates additional behaviour to the {@link AuthenticationSuccessHandler}.</li> * </ol> * * Subclasses can override this method to continue the {@link FilterChain} after * successful authentication. * @param request * @param response * @param chain * @param authResult the object returned from the <tt>attemptAuthentication</tt> * method. * @throws IOException * @throws ServletException