【问题标题】:Securing Rest API using Spring Security with AJAX Authentication使用带有 AJAX 身份验证的 Spring Security 保护 Rest API
【发布时间】:2015-02-11 01:08:04
【问题描述】:

我在为我的 web 应用程序设置 AJAX 调用的安全性时遇到问题,该应用程序在后端使用 Spring Security 来验证我的请求。

我的应用中有某些端点,如下所示,我正在通过我的网站和移动应用(Andriod、iOS)访问它们。

我现在已经为所有 /api/* urls 添加了基本身份验证,但这在移动应用访问的情况下很好,但如果我在我的网站的 AJAX 调用中使用相同的身份验证,那么我需要将用户名和密码作为标题请求传递,就像this link 中提到的那样,但我不想向外界透露用户名和密码,因为任何人都可以通过在 JS 文件上查看源代码来看到它们。

现在我想要的是任何 具有 ROLE_API 的用户名和密码都可以访问 /api/* url。但是当我从网站访问这些 URL 时,当我需要使用 AJAX 请求执行任何 CRUD 操作时,如何以安全的方式传递用户名和密码,以至于没有其他 URL 可以访问 /api/* urls来自我的网站。

下面是我的 security-config.xml、AuthenticationManager 和 Controller 的代码。

spring-security.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:security="http://www.springframework.org/schema/security"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/security 
http://www.springframework.org/schema/security/spring-security-3.2.xsd">


<security:http auto-config="false" use-expressions="true"
    access-denied-page="/login" entry-point-ref="authenticationEntryPoint">
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/api/**" access="hasRole('ROLE_API')"/>
<security:intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/user/**" access="hasRole('ROLE_USER')"/>
<security:logout invalidate-session="true" logout-success-url="/login" logout-url="/logout" />
<security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER" />
<security:custom-filter ref="authenticationFilter" position="BASIC_AUTH_FILTER" />
</security:http>

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

<bean id="authenticationFilter"
    class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
    p:authenticationManager-ref="customAuthenticationManager"
    p:authenticationFailureHandler-ref="customAuthenticationFailureHandler"
    p:authenticationSuccessHandler-ref="customAuthenticationSuccessHandler" />

<bean id="customAuthenticationManager"
    class="com.lumin.mytalk.authentication.CustomAuthenticationManager" />

<bean id="customAuthenticationFailureHandler"
    class="com.lumin.mytalk.authentication.AuthenticationFailureUrlHandler" />

<bean id="customAuthenticationSuccessHandler"
    class="com.lumin.mytalk.authentication.AuthenticationUrlHandler" />

<bean id="authenticationEntryPoint"
    class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"
    p:loginFormUrl="/login" />

    <security:authentication-manager />
</beans>

CustomAuthenticationManager.java

public class CustomAuthenticationManager implements AuthenticationManager {
protected static Logger logger = Logger
        .getLogger(CustomAuthenticationManager.class.getName());

@Autowired
private UserService userService;

private Md5PasswordEncoder passwordEncoder = new Md5PasswordEncoder();

public Authentication authenticate(Authentication auth)
        throws AuthenticationException {

    System.out.println("Performing custom authentication");

    // Init a database user object
    User user = null;
    if (auth != null && auth.getName() != null && !auth.getName().equals("")) {
        try {
            // Retrieve user details from database
            String username = auth.getName();
            user = userService.getByEmail(username);
        } catch (Exception e) {
            System.out.println("User does not exists!");
            throw new BadCredentialsException("User does not exists!");
        }

        if(user.getIsSocialUser() != null && user.getIsSocialUser()){
            return new UsernamePasswordAuthenticationToken(auth.getName(), null, getAuthorities(user.getRole()));
        }else{
            String pwd = (String) auth.getCredentials();
            if (passwordEncoder.encodePassword(pwd, null).equals(
                    user.getPassword())) {
                System.out.println("Correct Password!");
                System.out.println("User details are good and ready to go");
                return new UsernamePasswordAuthenticationToken(auth.getName(), auth.getCredentials(), getAuthorities(user.getRole()));
            } else {
                System.out.println("Incorrect Password");
                throw new BadCredentialsException("Incorrect Password !");
            }   
        }
    } else {
        System.out.println("Username and Password are required");
        throw new BadCredentialsException("Username and Password are required !");
    }
}

public Collection<GrantedAuthority> getAuthorities(String role) {
    // Create a list of grants for this user
    List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(0);
    // Check if this user has admin access
    if (role.equals(USERROLE.ROLE_USER.name())) {
        System.out.println("Grant ROLE_USER to this user");
        authList.add(new SimpleGrantedAuthority(USERROLE.ROLE_USER.name()));
    } else if (role.equals(USERROLE.ROLE_ADMIN.name())) {
        System.out.println("Grant ROLE_ADMIN to this user");
        authList.add(new SimpleGrantedAuthority(USERROLE.ROLE_ADMIN.name()));
    }
    // Return list of granted authorities
    return authList;
}
}

样品架控制器

@RestController
public class CategoryController extends AbstractGenericController{

    @Autowired
    private CategoryService categoryService;    

    @ResponseBody
    @RequestMapping(value = Path.Url.API + Path.Url.CATEGORY + Path.OperationUrl.CREATE, method = RequestMethod.POST, produces = { "application/json", "application/xml" })
    public Object create(@RequestBody Category category) {
        Category isExisting = categoryService.getCategoryByName(category.getCategoryName());
        if (isExisting == null) {
            Long id = categoryService.createWithID(category);
            return new Response(null, STATUS.SUCCESS, "Category created successfully. Id :" + id);
        }else{
            return new Response(null, STATUS.FAILURE, "Category already exist. Please use update category function.");
        }
    }
}

【问题讨论】:

  • 你有什么问题?为什么将用户名和密码存储在 JS 文件中?我希望每个用户都不同。
  • @zeroflagL 我想要的是带有 ROLE_API 的用户名和密码可以访问 /api/* url。但是当我从网站访问这些 URL 时,当我需要使用 AJAX 请求使用相同的 URL 进行任何 CRUD 操作时,我如何将数据传递给 /api/* urls
  • 您的评论和更新不会让事情变得更清楚。假设有一个用户 john。这个用户可以同时拥有ROLE_USERROLE_API,对吗?然后在某些时候必须输入凭据并将这些凭据发送到服务器。显然你知道怎么做,所以我仍然没有看到你的问题。如果您只有一个用户使用ROLE_API,那么您做错了什么。这基本上就像根本没有身份验证一样。

标签: java ajax spring-mvc spring-security


【解决方案1】:

用户名和密码可以base64编码并在Authentication header中传递。

【讨论】:

    猜你喜欢
    • 2013-12-05
    • 1970-01-01
    • 2016-10-28
    • 2019-02-07
    • 2017-04-15
    • 2014-02-21
    • 2014-03-18
    • 2016-03-20
    • 2014-07-03
    相关资源
    最近更新 更多