Spring Security Method Security Annotations

2019-08-12 18:59发布

问题:

Recently, I have begun a bit of a personal project, and I decided to implement Spring Security. I have attempted to do so before, but I had no better luck that time than I am now. I got around the problem then, but that method (accessing the security context directly from the code and checking the string of roles it contains for the current user) feels like a hack, and I would like to get it right this time.

Right now I have Spring Security MOSTLY functioning, as far as I know...I can attempt to go to a page with a role-based redirect, and it will redirect me to the login page. I can log in with good or bad information and be sent to the proper location. What I cannot do, what I have never managed to do, is get the @Secured or @PreAuthorize annotation to function as I would hope.

Let me try to explain (code will follow). My welcome/login page is index.jsp and, when you log in, Spring Security sends you to login.html which is where I have a method in my LoginController class mapped. Inside that method, I try to call a large set of other methods: none of this is supposed to be final, I am just trying to get prove to myself that things are running.

I call two methods that are secured by @Secured, and two methods that are secured by @PreAuthorize, one "ROLE_ADMIN" and one "ROLE_USER" each. The account that I am logging into only has the ROLE_USER authority. This being the case, I would expect to get redirected to my accessdenied.jsp page as per having set that as the target of my Spring Security's access-denied-page attribute. What I do not expect, and what I am seeing, is that every method is successfully called and run when I log in.

I have (at least attempted to) follow the tutorials. I have spent days on Google, reading everything that I can find. I have merged my security context into my context, and everything else that came to my attention as a potential solution. I apologize if I have been a mite long-winded, but I would rather provide too much information than too little. To that end, the following is my code:

index.jsp

    <html>
    <body>
        <form action="j_spring_security_check" method="POST">
            <label for="j_username">Name:</label> 
            <input id="j_username" type='text' name='j_username' /> 
            <br /> 
            <label for="j_password" class="passwordField">Password:</label> 
            <input id="j_password" type='password' name='j_password' /> 
            <br />
            <input id="proceed" type="submit" value="Submit" />
        </form>
    </body>
    </html>

LoginController.java

package cribbage.controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import org.springframework.jdbc.core.JdbcTemplate;

import cribbage.database.entity.Test;

@Controller
public class LoginController {
    @Autowired
    JdbcTemplate t;

    @RequestMapping(value = "/login")
    public ModelAndView login(HttpServletRequest request) {
        test();
        test2();
        test3();
        test4();
        return new ModelAndView("test.jsp");
    }

    @Secured("ROLE_ADMIN")
    public void test(){
        System.out.println("Test One");
    }

    @Secured("ROLE_USER")
    public void test2(){
        System.out.println("Test Two");
    }

    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public void test3(){
        System.out.println("Test Three");
    }

    @PreAuthorize("hasRole('ROLE_USER')")
    public void test4(){
        System.out.println("Test Four");
    }
}

web.xml

<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
                        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<display-name>Spring Security Tutorial Application</display-name>

<!-- - Location of the XML file that defines the root application context 
    - Applied by ContextLoaderListener. -->

<context-param>
    <description>Spring context file</description>
    <param-name>contextConfigLocation</param-name>
    <param-value>
       /WEB-INF/applicationContext.xml
       /WEB-INF/applicationContext-security.xml
   </param-value>
</context-param>

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!-- - Provides core MVC application controller. See bank-servlet.xml. -->
<servlet>
    <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/applicationContext.xml
            /WEB-INF/applicationContext-security.xml
        </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

applicationContext.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:security="http://www.springframework.org/schema/security"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
                    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd            
                    http://www.springframework.org/schema/tx
                    http://www.springframework.org/schema/tx/spring-tx-3.1.xsd         
                    http://www.springframework.org/schema/context 
                    http://www.springframework.org/schema/context/spring-context-3.1.xsd            
                    http://www.springframework.org/schema/aop 
                    http://www.springframework.org/schema/aop/spring-aop.xsd
                    http://www.springframework.org/schema/mvc
                    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
                    http://www.springframework.org/schema/security 
                    http://www.springframework.org/schema/security/spring-security-3.1.xsd">

<context:property-placeholder location="classpath:*.properties" />

<mvc:annotation-driven />

<!-- Which packages to scan when looking for beans defined with @Component -->
<context:component-scan scoped-proxy="targetClass"
    base-package="cribbage.controller
                  cribbage.database.dao
                  cribbage.database.entity" />
<context:annotation-config />

<!-- Turn on AspectJ @Configurable support -->

<!-- Turn on @Autowired, @PostConstruct etc support -->
<bean
    class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean
    class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />

<!-- Add Transaction support -->
<!-- Use @Transaction annotations for managing transactions -->
<tx:annotation-driven transaction-manager="txManager" />

<bean id="txManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

<bean id="messageSource"
    class="org.springframework.context.support.ResourceBundleMessageSource" />

<bean id="localeResolver"
    class="org.springframework.web.servlet.i18n.SessionLocaleResolver"
    p:defaultLocale="en_US" />

<!-- For database, uses maven filtering to fill in place holders -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
    <property name="driverClassName" value="${db.driver}" />
    <property name="url" value="${db.url}" />
    <property name="username" value="${db.username}" />
    <property name="password" value="${db.password}" />
    <property name="maxActive" value="10" />
    <property name="maxIdle" value="1" />
</bean>

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <constructor-arg ref="dataSource" />
</bean>

<security:global-method-security
    secured-annotations="enabled" pre-post-annotations="enabled" />

applicationContext-security.xml

<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
                http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                http://www.springframework.org/schema/security 
                http://www.springframework.org/schema/security/spring-security-3.1.xsd">

<http pattern="/CSS/**" security="none" />

<http auto-config="true" use-expressions="true" access-denied-page="/accessdenied.jsp">
    <form-login always-use-default-target="false"
        login-processing-url="/j_spring_security_check" default-target-url="/login.html"
        login-page="/index.jsp" authentication-failure-url="/loginFailed.jsp" />
    <logout logout-url="/j_spring_security_logout" />
    <intercept-url pattern="/test.jsp" access="hasRole('ROLE_USER')" />
</http>

<authentication-manager>
    <authentication-provider>
        <jdbc-user-service data-source-ref="dataSource"
            users-by-username-query="select username,user_password,enabled from users where username=?"
            authorities-by-username-query="select username,authority,enabled from users where username=?" />
    </authentication-provider>
</authentication-manager>

Thank you for any help that you can provide.

回答1:

actully spring security works only if aspect/security interceptor involved. In your code test(),test2(),test3(),test4() are invoked directly from controller method login. so there will not be any aspect involvement causing security to be bypassed.

if test methods are part of another spring bean then this should work as you have been expecting.

or if they are in same class then it should be invoked with spring bean instead of this (current object).