Spring Security 4 Rest API

来源:互联网 发布:端口攻击 编辑:程序博客网 时间:2024/06/07 22:44

有霾走遍天下,无霾寸步难行

最近在用spring boot 注解方式 实现 rest 风格的 angular 2 API, 有坑,虽记录之,如下:

Spring Security

是spring出的一套集认证与授权一体的库。官方文档大部分都是以网页提交表单的方式说明的。

Spring Security 认证源码

  1. Spring Security 4 认证代码在UsernamePasswordAuthenticationFilter 过滤器中,此过滤器继承AbstractAuthenticationProcessingFilterUsernamePasswordAuthenticationFilter 过滤器官方源码:
package org.springframework.security.web.authentication;import org.springframework.security.authentication.AuthenticationServiceException;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.Authentication;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import org.springframework.util.Assert;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class UsernamePasswordAuthenticationFilter extends        AbstractAuthenticationProcessingFilter {    /* 用户名 举个使用栗子:<form><input name="username"></form> */    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";    /* 密码 举个使用栗子:<form><input name="password"></form> */    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;    /* 登录只允许POST提交 */    private boolean postOnly = true;    /* 添加 /login 登录访问 */     public UsernamePasswordAuthenticationFilter() {        super(new AntPathRequestMatcher("/login", "POST"));    }    /* 认证用户帐号和密码 */    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);    }    protected String obtainPassword(HttpServletRequest request) {        return request.getParameter(passwordParameter);    }    protected String obtainUsername(HttpServletRequest request) {        return request.getParameter(usernameParameter);    }    protected void setDetails(HttpServletRequest request,            UsernamePasswordAuthenticationToken authRequest) {        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));    }    public void setUsernameParameter(String usernameParameter) {        Assert.hasText(usernameParameter, "Username parameter must not be empty or null");        this.usernameParameter = usernameParameter;    }    public void setPasswordParameter(String passwordParameter) {        Assert.hasText(passwordParameter, "Password parameter must not be empty or null");        this.passwordParameter = passwordParameter;    }    public void setPostOnly(boolean postOnly) {        this.postOnly = postOnly;    }    public final String getUsernameParameter() {        return usernameParameter;    }    public final String getPasswordParameter() {        return passwordParameter;    }}
  1. 如果需要自定义认证(本例子不需要重写),需重写UsernamePasswordAuthenticationFilter过滤器,
    并在WebSecurityConfiguration.configure(HttpSecurity http){}方法中添加此过滤器。源码往下看

解决跨域问题,一个CSRF和另一个CORS

  1. CSRF(Cross-Site Request Forgery,跨站点伪造请求)
  2. CORS (Cross Origin Resourse-Sharing, 跨域资源共享)
  3. .csrf().disable() 关闭CSRF
  4. .addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class) 允许跨域请求资源。
  5. .httpBasic()也是要添加的。
import org.springframework.beans.factory.annotation.Autowired;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.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.config.http.SessionCreationPolicy;import org.springframework.security.web.access.channel.ChannelProcessingFilter;import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration@EnableWebSecuritypublic class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {    @Override    protected void configure(HttpSecurity http) throws Exception {        http                .csrf().disable()                .httpBasic()                    .and()                .addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class)                .addFilterBefore(new MyUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)                .authorizeRequests()                    .antMatchers("/cnvd/list/1").authenticated()                    .anyRequest().permitAll()                    .and()                .formLogin()                    .successHandler(new MyAuthenticationSuccessHandler())                    .failureHandler(new MyAuthenticationFailureHandler())                    .and()                .logout()                    .permitAll();    }    @Autowired    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {        auth                .inMemoryAuthentication()                .withUser("user").password("password").roles("USER");    }}

CORSFilter过滤器

import javax.servlet.*;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class CORSFilter implements Filter {    @Override    public void init(FilterConfig filterConfig) throws ServletException {    }    @Override    public void doFilter(ServletRequest request, ServletResponse res, FilterChain chain) throws IOException, ServletException {        HttpServletResponse response = (HttpServletResponse) res;        response.setHeader("Access-Control-Allow-Origin", "*");        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");        response.setHeader("Access-Control-Max-Age", "3600");        response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, x-requested-with, Cache-Control");        chain.doFilter(request, response);    }    @Override    public void destroy() {    }}

认证之后返回JSON数据,而非重定向(Redirect)

  1. 实现AuthenticationSuccessHandlerAuthenticationFailureHandler
  2. WebSecurityConfiguration.configure(HttpSecurity http){}方法中启用。参考:WebSecurityConfiguration

AuthenticationSuccessHandler源码

import org.springframework.context.annotation.Configuration;import org.springframework.security.core.Authentication;import org.springframework.security.web.WebAttributes;import org.springframework.security.web.authentication.AuthenticationSuccessHandler;import org.springframework.security.web.savedrequest.HttpSessionRequestCache;import org.springframework.security.web.savedrequest.RequestCache;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.io.IOException;import java.io.PrintWriter;public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler{    private RequestCache requestCache = new HttpSessionRequestCache();    @Override    public void onAuthenticationSuccess(HttpServletRequest request,                                        HttpServletResponse response, Authentication authentication)            throws IOException, ServletException {        PrintWriter writer;        String returnStr = "{\"message\":\"sucess\"}";        response.setStatus(200);        writer = response.getWriter();        writer.write(returnStr);        writer.flush();        writer.close();        requestCache.removeRequest(request, response);        clearAuthenticationAttributes(request);    }    protected final void clearAuthenticationAttributes(HttpServletRequest request) {        HttpSession session = request.getSession(false);        if (session == null) {            return;        }        session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);    }    public void setRequestCache(RequestCache requestCache) {        this.requestCache = requestCache;    }}

AuthenticationFailureHandler源码

import org.springframework.context.annotation.Configuration;import org.springframework.security.core.Authentication;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.authentication.AuthenticationFailureHandler;import org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler;import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler  {    @Override    public void onAuthenticationFailure(HttpServletRequest request,                                        HttpServletResponse response, AuthenticationException exception)            throws IOException, ServletException {        PrintWriter writer;        String returnStr = "{exception:{name:'" + exception.getClass()                + "',message:'" + exception.getMessage() + "'}}";        System.out.println(this.getClass().toString()+":"+returnStr);        writer = response.getWriter();        writer.write(returnStr);        writer.flush();        writer.close();    }}

Spring Security 授权

稍后更新

这里有个结语

  • CORSFilter有个注解的写法corsConfigurer()函数相关,但是对Spring Security默认的 /login 无效,其他自定义Controller请求可以。
  • 有关CSRF和CORS可以参考:http://www.cnblogs.com/lailailai/p/4528092.html
0 0
原创粉丝点击