zoukankan      html  css  js  c++  java
  • SpringInAction 第四章笔记 保护Spring

    项目地址:https://github.com/AganRun/SpringInAction/tree/master/Chapter4/taco-security

    第四章 配置Spring Security

    初次使用

    当加入了starter启动器后,项目启动时,在日志中会出现一个账号及随机密码

    Using generated security password: cf18a7e9-ffa6-429f-9331-40d2dd121f53
    

    此时登录项目会有一个登录页面,输入账号密码才能访问

    security starter的影响

    • 所有HTTP请求都需要认证,认证过程是通过HTTP basic认证对话框实现的
    • 没有特定的角色及权限,只有一个user用户
    • 没有登录页面

    配置Security

    只有一个用户显然满足不了需求,security有四种配置用户的方式

    • 基于内存的用户存储 (将账号密码直接硬编码到配置代码中,优点:方便快捷,可以用来调试。缺点:项目上线后不方便修改用户)
    • 基于JDBC的用户存储
    • 以LDAP作为后端的用户存储
    • 自定义用户详情服务

    JDBC配置Security

    当配置了数据源之后,Spring有一套默认的用户搜索SQL。若表于其不匹配,则可以自定义SQL去配置用户。

    酱默认的SQL查询替换为自定义的设计时,很重要的一点是遵循查询的基本协议。所有查询将用户名作为唯一参数

    @Configuration
    @EnableWebSecurity
    public class SecurityJdbcConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        DataSource dataSource;
        
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth
                .jdbcAuthentication()
                    .dataSource(dataSource)
                    .usersByUsernameQuery(
                            "select username, password, enabled from Users where username = ?"
                    )
                    .authoritiesByUsernameQuery(
                                "select username, authority from UserAuthorities where username = ?"
                    );
        }
    }
    

    密码加密

    数据库明文存储密码是很危险的,passwordEncoder()方法可以接受任意的PasswordEncoder接口的实现

    • BCryptPasswordEncoder: 使用bcrypt强哈希加密
    • NoOpPasswordEncoder: 不进行任何转码(已过期)
    • Pbkdf2PasswordEncoder: 使用PBKDF2加密
    • SCryptPasswordEncoder: 使用scrypt哈希加密
    • StandardPasswordEncoder: 使用SHA-256哈希加密

    逻辑是:数据库中的密码应该永远不会被解密。将输入的密码进行算法转码,然后与库中的密码对比。

    @Bean
    public static PasswordEncoder passwordEncoder() {
        //自定义一个加密流程
        return new PasswordEncoder() {
            @Override
            public String encode(CharSequence charSequence) {
                //加1加密,哈哈
                System.out.println("encode" + charSequence);
                return charSequence.toString() + 1;
            }
    
            @Override
            public boolean matches(CharSequence charSequence, String s) {
                System.out.println("match,charSequence:" + charSequence + ", dbPassword:" + s);
                return encode(charSequence).equals(s);
            }
        };
    }
    
    
    DB: user1/12341
    输入: user1/1234
    
    控制台输出
    match,charSequence:1234, dbPassword12341
    encode1234
    

    自定义用户认证

    1. 自定义一个实体类,例如User,去实现UserDetails接口
    2. 定义对应的Repository,Service(需要实现UserDetailsService的loadUserByUsername(String username))
    3. 配置类中,auth.userDetailsService(注入的service)

    保护Web请求

    在WebSecurityConfigureAdapter的Configure(HttpSecurity http)方法中
    可以配置的功能有:

    • 为某个请求提供服务之前,先预先满足条件
    • 配置自定义的登录页
    • 支持用户退出应用
    • 预防跨站请求伪造

    保护请求 & 登录

    可以通过配置路径与对应的角色控制,并指定登录页

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/design", "/orders")
    //                    .hasRole("USER")
                    .access("hasRole('USER')")     // design 和 orders 路径,需要有user角色
                .antMatchers("/", "/**")
    //                    .permitAll();
                    .access("permitAll")    //对于/和/**路径不拦截
            .and()
                .formLogin()
                    .loginPage("/login")   //登录页面
                    .defaultSuccessUrl("/design")  //成功后重定向到design页面
            ;
    }
    

    存在很多的配置方法,列举其中的几个

    方法 能做什么
    access(String) 如果给定的SpEL表达式计算结果为TRUE,就允许访问
    authenticated() 允许认证过的用户访问
    denyAll() 无条件拒绝所有访问
    hasIpAddress(String) 如果请求来自给定的IP地址,允许访问
    hasRole(String) 如果用户具备指定的角色,允许访问
    not() 对其他方法的结果求反
    permitAll() 无条件允许访问
    rememberMe() 如果用户通过Remember-me认证的,允许访问

    SpEl表达式可以编写复杂的逻辑。例如只允许具备ROLE_USER权限的用户,在星期二下午创建新的Taco

    退出

    .and().logout().logoutSuccessUrl("/");
    

    POST请求403?

    在上述配置完之后,即使用户已经登录了,在提交订单,请求/design时依旧会被拦截,页面403。
    那是因为SpringSecurity默认是开启了内置的CSRF保护, 建议不要关掉。先了解一下什么是CSRF。

    CSRF

    跨站请求伪造(Cross-Site Request Forgery, CSRF)。
    它会让用户在一个恶意Web页面填写信息,然后自动将表单以攻击受害者的身份提交到另外一个应用上。

    可以理解为:攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。

    预防措施 :应用在展现表单的时候,生成一个CSRF token,放在隐藏域存储起来,在提交表单的时候将token一起发送至服务器,由服务器对比匹配。

    可以加入隐藏域(JSP、Thymeleaf默认生成)

    <input type="hidden" name="_csrf" th:value="${_csrf.token}" />
    

    提交表单时,使用th:action属性

    <form method="POST" th:action="@{/login}" id="loginForm" >
    

    了解用户是谁

    如何获取当前用户的信息

    • 注入Principal对象到控制器
    • 注入Authentication对象到控制器
    • 使用@AuthenticationPricipal注解标注方法
    • 使用SecurityContextHolder来获取安全上下文
    public String processDesign(@Valid Taco taco, Errors errors, @ModelAttribute Order order, Principal principal) {
        String username = principal.getName()   //获取用户的username=>获取用户信息  缺点:业务中会掺杂安全代码
    
    public String processDesign(@Valid Taco taco, Errors errors, @ModelAttribute Order order, Authentication authentication) {
        User user = (User) authentication.getPrincipal();       //强转User对象
    
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        User user = (User) authentication.getPrincipal();       //繁琐,但是任何地方都可以,不限制在Controller中
    
    public String processDesign(@Valid Taco taco, Errors errors, @ModelAttribute Order order, @AuthenticationPrincipal User user) {
        //使用注解,最方便
    }
    
  • 相关阅读:
    Quagga How to use Quagga
    Quagga Case 4
    Quagga Case 3
    Quagga Case 2
    Quagga Routing Suite
    quagga
    quagga 的原理解析 zebra原理解析
    zebra线程管理源码简析
    【习题 4-8 UVA
    【习题 4-7 UVA
  • 原文地址:https://www.cnblogs.com/AganRun/p/13155168.html
Copyright © 2011-2022 走看看