Spring Security 使用基于xml的http认证

Posted

技术标签:

【中文标题】Spring Security 使用基于xml的http认证【英文标题】:Spring security using http authentication based on xml 【发布时间】:2015-02-22 13:51:07 【问题描述】:

我正在尝试在我的项目中实现弹簧安全性,我的要求是

第一个用户将使用将生成安全代码的 url 登录 -

http://localhost:8181/SpringSecurity/login

成功登录后,我点击了安全的 API 调用,例如 -

http://localhost:8181/SpringSecurity/admin with secured key generated by login method

我正在使用 crome postman 来访问 API

虽然我使用具有 ROLE_ADMIN 的用户登录,但仍然不允许我访问受保护的 API

我的 web.xml 是

 <?xml version="1.0" encoding="UTF-8"?>
<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">

<!-- The definition of the Root Spring Container shared by all Servlets 
    and Filters -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Processes application requests -->
<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/spring/spring-security.xml         
    </param-value>
</context-param>

<!-- Spring Security -->
<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>


</web-app>

servlet-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

<mvc:annotation-driven/>

<mvc:resources mapping="/resources/**" location="/resources/" />

<bean
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/" />
    <property name="suffix" value=".jsp" />
</bean>

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">

    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/test" />
    <property name="username" value="root" />
    <property name="password" value="root" />
</bean>

<bean id="sessionFactory"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan" value="com.ha.**"/>     
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
            <prop key="hibernate.show_sql">false</prop>
        </props>
    </property>
</bean>

<bean id="txManager"
    class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="persistenceExceptionTranslationPostProcessor"
    class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

<bean id="savedRequestAwareAuthenticationSuccessHandler"
    class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
    <property name="targetUrlParameter" value="targetUrl" />
</bean>

<context:component-scan base-package="com.ha.**" />

<bean id="jsonConverter"
    class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
    <property name="prefixJson" value="false" />
    <property name="supportedMediaTypes" value="application/json" />
</bean>

<import resource="../spring-security.xml"/>

spring-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"
xmlns:context="http://www.springframework.org/schema/context"
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.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

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

<http auto-config="true">
    <intercept-url pattern="/admin**" access="ROLE_ADMIN" />
    <custom-filter ref="tokenProcessingFilter" after="FORM_LOGIN_FILTER" />
</http>

<beans:bean
    class="com.ha.security.AuthenticationTokenAndSessionProcessingFilter"
    id="tokenProcessingFilter">
    <beans:constructor-arg name="principal" value="ANONYMOUS" />
    <beans:constructor-arg name="authority" value="anonymousUser" />
    <beans:constructor-arg name="tokenStore" ref="inMemoryTokenStore" />
</beans:bean>

<beans:bean class="com.ha.security.InMemoryTokenStore" id="inMemoryTokenStore" />

<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="hr" password="123456" authorities="ROLE_USER" />
        </user-service>
    </authentication-provider>
</authentication-manager>

控制器文件是

      package com.ha.security;

  import java.util.ArrayList;
  import java.util.List;

  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.http.HttpStatus;
  import org.springframework.http.MediaType;
  import org.springframework.security.access.annotation.Secured;
  import org.springframework.security.core.GrantedAuthority;
  import org.springframework.security.core.authority.SimpleGrantedAuthority;
  import org.springframework.security.core.userdetails.User;
  import org.springframework.stereotype.Controller;
  import org.springframework.util.StringUtils;
  import org.springframework.web.bind.annotation.RequestBody;
  import org.springframework.web.bind.annotation.RequestMapping;
  import org.springframework.web.bind.annotation.RequestMethod;
  import org.springframework.web.bind.annotation.ResponseBody;
  import org.springframework.web.bind.annotation.ResponseStatus;

  import com.google.gson.Gson;
  import com.google.gson.GsonBuilder;
  import com.ha.model.UserEntity;
  import com.ha.services.IUserService;

  /**
   * Handles requests for the application home page.
   */
  @Controller
  public class HomeController 

     @Autowired
     private InMemoryTokenStore tokenStore;

     @Autowired
     IUserService userServices;

  /*    @RequestMapping(value =  "/" , method = RequestMethod.GET)
     public String welcomePage() 
        return "index";

       */

     @RequestMapping(value =  "/", "/welcome**" , method = RequestMethod.GET)
     @Secured("ROLE_ADMIN")
     public @ResponseBody String defaultPage() 

         List<UserEntity> userEntity = userServices.getUsersList();
         GsonBuilder builder = new GsonBuilder();
          Gson gson = builder.create();
          return gson.toJson(userEntity);
     

     @RequestMapping(value = "/admin", method = RequestMethod.GET, produces =  MediaType.APPLICATION_JSON_VALUE )
     @ResponseStatus(value = HttpStatus.OK)
     @ResponseBody
     public ResponseDto adminPage() 
        return new ResponseDto("Can Access Admin");
     

     @RequestMapping(value = "/login", method = RequestMethod.POST, produces =  MediaType.APPLICATION_JSON_VALUE )
     @ResponseStatus(value = HttpStatus.OK)
     @ResponseBody
     public ResponseDto login(@RequestBody UserLoginDto loginDto) 
        String userName = loginDto.getUserName();
        String password = loginDto.getPassword();   
        if (StringUtils.hasText(userName) && StringUtils.hasText(password)
              && userServices.validateAdminUser(loginDto)) 
           ArrayList<GrantedAuthority> objAuthorities = new ArrayList<GrantedAuthority>();
           SimpleGrantedAuthority objAuthority = new SimpleGrantedAuthority(
                 "ROLE_ADMIN");
           objAuthorities.add(objAuthority);
           User user = new User(userName, password, objAuthorities);
           return new ResponseDto(this.tokenStore.generateAccessToken(user));
         else 
           return new ResponseDto("Not Valid User");
        
     
  

AuthenticationTokenAndSessionProcessingFilter

package com.ha.security;
import java.io.IOException;
import java.util.List;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;

public class AuthenticationTokenAndSessionProcessingFilter extends
    GenericFilterBean 

private final InMemoryTokenStore tokenStore;

private final Object principal;
private final List<GrantedAuthority> authorities;

public AuthenticationTokenAndSessionProcessingFilter(
        InMemoryTokenStore tokenStore, String authority, String principal) 
    this.tokenStore = tokenStore;
    this.principal = principal;
    this.authorities = AuthorityUtils.createAuthorityList(authority);


@Override
public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException 
    if (!(request instanceof HttpServletRequest)) 
        throw new RuntimeException("Expecting a HTTP request");
    

    HttpServletRequest httpRequest = (HttpServletRequest) request;

    String authToken = null;
    UserDetails objUserDetails = null;
    if (StringUtils.hasText(httpRequest.getHeader("Authorization"))) 
        authToken = httpRequest.getHeader("Authorization");
        objUserDetails = this.tokenStore
                .readAccessToken(authToken);
    
    setAuthentication(objUserDetails, httpRequest);
    chain.doFilter(request, response);



private void setAuthentication(UserDetails objUserDetails,
        HttpServletRequest request) 
    UsernamePasswordAuthenticationToken authentication = null;
    if (null != objUserDetails) 
        authentication = new UsernamePasswordAuthenticationToken(
                objUserDetails, null, objUserDetails.getAuthorities());
        authentication.setDetails(new WebAuthenticationDetailsSource()
                .buildDetails(request));
        SecurityContextHolder.getContext()
                .setAuthentication(authentication);
     else 
        authentication = new UsernamePasswordAuthenticationToken(
                this.principal, null, this.authorities);
        SecurityContextHolder.getContext()
                .setAuthentication(authentication);
    


【问题讨论】:

我看不出用户应该在哪里/为什么应该得到ROLE_ADMIN? - 但问题可能只是您需要给用户一个新会话(或“手动”更新 Authories 对象) 我正在从数据库验证用户请检查控制器中的登录方法,它正在调用 userServices.validateAdminUser() 方法 我看到你在哪里检查 fpr 角色,我看到了奇怪的(不是 spring secuity 连接的身份验证),但我没有看到任何代码将身份验证中的“授予”角色与 spring-安全框架。 @Ralph,您能否建议我在我的代码中进行所需的更改,因为我是 Spring MVC 的新手 你的问题不是你的 Spring-MVC 知识,你需要了解更多关于 Spring-Security 的知识。您可以从发布在此站点上的“Spring Security 入门”一书的摘录开始:packtpub.com/books/content/getting-started-spring-security 【参考方案1】:

相信你需要使用hasRole方法

<intercept-url pattern="/admin**" access="hasRole('ROLE_ADMIN')" />

我还注意到您的用户列表中只有一个用户,角色为"ROLE_USER",希望您也添加了管理员用户。

【讨论】:

需要hasRoleuse-expressionshttp标签中启用,但不是,所以access="ROLE_ADMIN"是正确的【参考方案2】:

感谢您的支持,问题现在得到解决,它是导入安全操作的样式。我多次导入安全 xml 文件

我在

中有导入安全性

web.xml

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/spring/spring-security.xml         
    </param-value>
</context-param>

以及在 spring-context.xml 文件中

<import resource="../spring-security.xml"/>

由于 inMemoryTokenStore 创建了多个实例,并且在验证它时检查了不同的用户令牌。

从 spring-context.xml 中删除条目并在 HomeController 中添加以下限定符后,我的代码工作正常

@Qualifier("inMemoryTokenStore")
private InMemoryTokenStore tokenStore;

谢谢evertybuddy....:)

【讨论】:

以上是关于Spring Security 使用基于xml的http认证的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security应用开发(02)基于XML配置的用户登录

如何同时为 Spring Security 正确定义 web.xml 和基于 java 的配置

基于 XML 的 Spring Security 配置中的 use-expressions 属性等同于基于代码的配置

Spring Boot中Spring Security的XML配置

Java 配置中的 Spring Security XML 配置和 Spring SAML

Spring Security应用开发(20)基于方法的授权使用@RolesAllowed注解