基于rest的SpringSecurity(完整版,相对最安全版本)

3 篇文章 0 订阅
本文介绍了如何在Spring Security中构建基于REST的安全应用,强调了去除http.csrf().disable()以提高安全性,并提到真正安全的应用应考虑requiresChannel().antMatchers("/mysafe/**").requiresSecure()。内容涵盖自定义用户认证、HTTPS支持以及QML客户端的安全处理。
摘要由CSDN通过智能技术生成

相关文章

http://blog.csdn.net/makefriend7/article/details/53490184

http://blog.csdn.net/makefriend7/article/details/53514908

官网文档说明 http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/

这回要去除Security类中的http.csrf().disagbe()了

这个才算是相对安全一点的做法。当然,真正要安全的地方必须加上

requiresChannel().antMatchers("/mysafe/**").requiresSecure();

(关于SSL的介绍我准备再下一篇博客中研究),这篇的例子就不加这个开关了。


既然是完整版,用户的认证当然得采用自定义的方式

 
 

//安全类必须实现接口WebSecurityConfigurer 或者扩展WebSecurityConfigurerAdapter这里采用后者

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.util.matcher.RequestMatcher;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;

@Configuration
@EnableWebSecurity//启用Web安全
@ComponentScan
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Autowired
    private MySavedRequestAwareAuthenticationSuccessHandler authenticationSuccessHandler;

    @Autowired
    private SessionCsrfTokenRepository sessionCsrfTokenRepository;

    @Autowired
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;

这个函数主要说明登陆框的样式,地址等等(有默认登陆框)
    @Override
    protected void configure(HttpSecurity http) throws Exception {
//   ant通配符说明
//?	匹配任何单字符
// *	匹配0或者任意数量的字符
// **	匹配0或者更多的目录

        http.formLogin()
                .successHandler(authenticationSuccessHandler)
                .failureHandler(myAuthenticationFailureHandler).and()
                .authorizeRequests()
                .antMatchers("/login", "/myresource/**").permitAll()
                .antMatchers("/system1/**").hasRole("USER1")
                .antMatchers("/system2/**").hasRole("USER2")
                .antMatchers("/system3/**").hasAnyRole("USER1", "USER2")
                .anyRequest().authenticated();//.and()
    }

    ///这个函数主要说明需要认证的用户,密码,以及权限
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(new MyUserDetailService());
    }


}


如果要支持https代码如下

@Configuration
@EnableWebSecurity//启用Web安全
@ComponentScan
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Autowired
    private MySavedRequestAwareAuthenticationSuccessHandler authenticationSuccessHandler;

    @Autowired
    private SessionCsrfTokenRepository sessionCsrfTokenRepository;

    @Autowired
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {


        http.formLogin()
                .successHandler(authenticationSuccessHandler)
                .failureHandler(myAuthenticationFailureHandler).and()
                .requiresChannel()
               .antMatchers("/login").requiresSecure()
                .antMatchers("/system1/**").requiresSecure()
                .antMatchers("/system2/**").requiresSecure().and()
                .authorizeRequests()
                .antMatchers("/login", "/myresource/**").permitAll()
                .antMatchers("/system1/**").hasRole("USER1")
                .antMatchers("/system2/**").hasRole("USER2")
                .antMatchers("/system3/**").hasAnyRole("USER1", "USER2")
                .anyRequest().authenticated();//.and()
    }

    ///这个函数主要说明需要认证的用户,密码,以及权限
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(new MyUserDetailService());
    }




这里我们对用户认证失败做了自己的处理, 对系统的账号,密码,角色。做了自己的处理

失败处理代码如下

import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Component
public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
    private final static Logger logger = LoggerFactory.getLogger(MyAuthenticationFailureHandler.class);
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        logger.info("认证失败");
        JSONObject responseJSONObject = new JSONObject();
        responseJSONObject.put("info","login failed");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        PrintWriter out = null;
        try {
            out = response.getWriter();
            out.append(responseJSONObject.toString());
            logger.debug("返回是\n");
            logger.debug(responseJSONObject.toString());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                out.close();
            }
        }
        super.onAuthenticationFailure(request, response, exception);
    }
}

自己定义账号,密码的读取方式(这个仅仅是展示,实际你可以在这个类中用你想用的任何技巧,比如从NOSQL 数据库读,从文件读,从redis读都是可以的。这个就不展开了)  唯一需要注意的是。默认的角色前面系统给加了 ROLE_

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

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

//这个类可以随便注入,比如读数据库就可以注入数据库的操作类
@Component
public class MyUserDetailService implements UserDetailsService{
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if (username.equals("user1")) {
            List<GrantedAuthority> authorityList = new ArrayList<GrantedAuthority>();
            SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_USER1");
            authorityList.add(simpleGrantedAuthority);
            User user = new User("user1", "password1", authorityList);
            return user;
        }
        if (username.equals("user2")) {
            List<GrantedAuthority> authorityList = new ArrayList<GrantedAuthority>();
            SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_USER2");
            authorityList.add(simpleGrantedAuthority);
            User user = new User("user2", "password2", authorityList);
            return user;
        }
        return null;
    }
}


QML客户端的代码比较麻烦。解析XML可以使用XmlListModel,不过这里为了简化处理,直接就取了位置

 function myLoginWithCsrf()
    {
        console.log("begin system fun1");
        var url1 = "http://172.16.129.15:8080/system1/fun1";
        var tmp1 = JSON;
        tmp1.username = "user1"
        tmp1.password = "password1";
        var mycontent = JSON.stringify(tmp1);
        //    myhttp.abort();
        myhttp.open("POST", url1, false); //简单化处理。都采用同步的方式
        myhttp.setRequestHeader("Content-type", "application/json");
        myhttp.setRequestHeader("Content-length", mycontent.length);
        myhttp.setRequestHeader("Connection", "Keep-Alive");
        myhttp.send(mycontent);//结果是非法的请求
        
        
        console.log("begin test myresource/resource1");
        url1 = "http://172.16.129.15:8080/myresource/resource1";
        myhttp.open("POST", url1, false); //简单化处理。都采用同步的方式
        myhttp.setRequestHeader("Content-type", "application/json");
        myhttp.setRequestHeader("Content-length", mycontent.length);
        myhttp.setRequestHeader("Connection", "Keep-Alive");
        myhttp.send(mycontent);//资源同样失败

        
        //获取login的XML文件
        console.log("get login xml");
        mycontent="username=user1&password=password1&submit=Login";
        url1 = "http://172.16.129.15:8080/login";
        myhttp.open("GET", url1, false); //简单化处理。都采用同步的方式
        myhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        myhttp.setRequestHeader("Content-length", mycontent.length);
        myhttp.setRequestHeader("Connection", "Keep-Alive");
        myhttp.send(mycontent);
        var docXml = myhttp.responseText;
        //此处应该用XmlListModel进行解析
        var docToken = docXml.substring(464, 500)
        console.log("docToken is " + docToken);
        
        //登陆,并拿到真正的Token
        console.log("begin mylogin");
        mycontent="username=user1&password=password1&submit=Login";
        url1 = "http://172.16.129.15:8080/login";
        myhttp.open("POST", url1, false); //简单化处理。都采用同步的方式
        myhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        myhttp.setRequestHeader("Content-length", mycontent.length);
        myhttp.setRequestHeader("Connection", "Keep-Alive");
        myhttp.setRequestHeader("X-CSRF-TOKEN", docToken);
        myhttp.send(mycontent);
        var docRealToken = myhttp.responseText;
        console.log("docRealToken is "+ docRealToken);
        
        console.log("begin test myresource/resource1");
        url1 = "http://172.16.129.15:8080/myresource/resource1";
        myhttp.open("POST", url1, false); //简单化处理。都采用同步的方式
        myhttp.setRequestHeader("Content-type", "application/json");
        myhttp.setRequestHeader("Content-length", mycontent.length);
        myhttp.setRequestHeader("Connection", "Keep-Alive");
        myhttp.setRequestHeader("X-CSRF-TOKEN", docRealToken);
        myhttp.send(mycontent);    //现在当然成功
        
        console.log("begin system fun1");
        url1 = "http://172.16.129.15:8080/system1/fun1";
        myhttp.open("POST", url1, false); //简单化处理。都采用同步的方式
        myhttp.setRequestHeader("Content-type", "x-www-form-urlencoded");
        myhttp.setRequestHeader("Content-length", mycontent.length);
        myhttp.setRequestHeader("Connection", "Keep-Alive");
        myhttp.setRequestHeader("X-CSRF-TOKEN", docRealToken);
        myhttp.send(mycontent); //有权限也没问题。
        
       
        console.log("22 begin system fun2");
        url1 = "http://172.16.129.15:8080/system2/fun2";
        myhttp.open("POST", url1, false); //简单化处理。都采用同步的方式
        myhttp.setRequestHeader("Content-type", "x-www-form-urlencoded");
        myhttp.setRequestHeader("Content-length", mycontent.length);
        myhttp.setRequestHeader("Connection", "Keep-Alive");
        myhttp.setRequestHeader("X-CSRF-TOKEN", docRealToken);
        myhttp.send(mycontent);//没权限肯定啥都拿不到。
        
        
        //改用用户2登陆
        console.log("chaneg to user2")
        console.log("get login xml again");
        mycontent="username=user1&password=password1&submit=Login";
        url1 = "http://172.16.129.15:8080/login";
        myhttp.open("GET", url1, false); //简单化处理。都采用同步的方式
        myhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        myhttp.setRequestHeader("Content-length", mycontent.length);
        myhttp.setRequestHeader("Connection", "Keep-Alive");
        myhttp.send(mycontent);
        docXml = myhttp.responseText;
        docToken = docXml.substring(464, 500)
        console.log("docToken again is " + docToken);
        
        console.log("begin mylogin again");
        mycontent="username=user2&password=password2&submit=Login";
        url1 = "http://172.16.129.15:8080/login";
        myhttp.open("POST", url1, false); //简单化处理。都采用同步的方式
        myhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        myhttp.setRequestHeader("Content-length", mycontent.length);
        myhttp.setRequestHeader("Connection", "Keep-Alive");
        myhttp.setRequestHeader("X-CSRF-TOKEN", docToken);
        myhttp.send(mycontent);
        docRealToken = myhttp.responseText;
        console.log("docRealToken "+ docRealToken);
        
        console.log("begin system fun1");
        url1 = "http://172.16.129.15:8080/system1/fun1";
        myhttp.open("POST", url1, false); //简单化处理。都采用同步的方式
        myhttp.setRequestHeader("Content-type", "x-www-form-urlencoded");
        myhttp.setRequestHeader("Content-length", mycontent.length);
        myhttp.setRequestHeader("Connection", "Keep-Alive");
        myhttp.setRequestHeader("X-CSRF-TOKEN", docRealToken);
        myhttp.send(mycontent);
        
        console.log("begin system fun1");
        url1 = "http://172.16.129.15:8080/system2/fun2";
        myhttp.open("POST", url1, false); //简单化处理。都采用同步的方式
        myhttp.setRequestHeader("Content-type", "x-www-form-urlencoded");
        myhttp.setRequestHeader("Content-length", mycontent.length);
        myhttp.setRequestHeader("Connection", "Keep-Alive");
        myhttp.setRequestHeader("X-CSRF-TOKEN", docRealToken);
        myhttp.send(mycontent);
        
        
    }


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值