Spring Security_2

作者: elijah777 | 来源:发表于2019-04-25 11:23 被阅读5次

    9.4 认证用户

    最简单的Spring Security配置的话,那么就能无偿地得到一个登录页。实际上,在重写configure(HttpSecurity)之前,我们都能使用一个简单却功能完备的登录页。但是,一旦重写了configure(HttpSecurity)方法,就失去了这个简单的登录页面。

    不过,把这个功能找回来也很容易。我们所需要做的就是在configure(HttpSecurity)方法中,调用formLogin(),如下面的程序清单所示。 请注意,和前面一样,这里调用add()方法来将不同的配置指令连接在一起。 如果我们访问应用的“/login”链接或者导航到需要认证的页面,那么将会在浏览器中展现登录页面。

    // 启用默认的登录页
    httpSecurity.formLogin()
     .and()
     .authorizeRequests()
     .antMatchers("/spitter/me").hasRole("SPITTER")
     .anyRequest().permitAll()
     .and()
     .requiresChannel()
     .antMatchers("/spitter/from")
     .requiresSecure(); 
    

    9.4.1 添加自定义的登录页

    需要注意的一个关键点是<form>提交到了什么地方。同时还需要注意username和password输入域,在你的登录页中,需要同样的输入域。最后,假设没有禁用CSRF的话,还需要保证包含了值为CSRF token的“_csrf”输入域。

    <input type="hidden" name="_csrf" value="6984_hsd2_wrwqc_wrqd" />
    

    在Thymeleaf模板中,包含了username和password输入域,就像默认的登录页一样,它也提交到了相对于上下文的“/login”页面上。因为这是一个Thymeleaf模板,因此隐藏的“_csrf”域将会自动添加到表单中

    
    <form method="POST" style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px;">
    

    9.4.2 启用HTTP Basic认证</form>

    参考 HTTP Basic 认证 https://blog.csdn.net/yhb241/article/details/80646485

    对于应用程序的人类用户来说,基于表单的认证是比较理想的。但是在第16章中,将会看到如何将我们Web应用的页面转化为RESTful API。当应用程序的使用者是另外一个应用程序的话,使用表单来提示登录的方式就不太适合了。

    HTTP Basic认证(HTTP Basic Authentication)会直接通过HTTP请求本身,对要访问应用程序的用户进行认证。你可能在以前见过HTTP Basic认证。当在Web浏览器中使用时,它将向用户弹出一个简单的模态对话框。

    但这只是Web浏览器的显示方式。本质上,这是一个HTTP 401响应,表明必须要在请求中包含一个用户名和密码。在REST客户端向它使用的服务进行认证的场景中,这种方式比较适合。

    如果要启用HTTP Basic认证的话,只需在configure()方法所传入的HttpSecurity对象上调用httpBasic()即可。另外,还可以通过调用realmName()方法指定域。如下是在Spring Security中启用HTTP Basic认证的典型配置:

    httpSecurity.formLogin()
     .and()
     .httpBasic()
     .realmName("Spittr")
    

    在httpBasic()方法中,并没有太多的可配置项,甚至不需要什么额外配置。HTTP Basic认证要么开启要么关闭。所以,与其进一步研究这个话题,还不如看看如何通过Remember-me功能实现用户的自动认证。

    9.4.3 启用Remember-me功能

    对于应用程序来讲,能够对用户进行认证是非常重要的。但是站在用户的角度来讲,如果应用程序不用每次都提示他们登录是更好的。这就是为什么许多站点提供了Remember-me功能,你只要登录过一次,应用就会记住你,当再次回到应用的时候你就不需要登录了。

    Spring Security使得为应用添加Remember-me功能变得非常容易。为了启用这项功能,只需在configure()方法所传入的HttpSecurity对象上调用rememberMe()即可。

    httpSecurity.formLogin()
     .loginPage("/login")
     .and()
     .rememberMe()
     .tokenValiditySeconds(2439800)
     .key("spittrKey")
     ....
    

    在这里,我们通过一点特殊的配置就可以启用Remember-me功能。默认情况下,这个功能是通过在cookie中存储一个token完成的,这个token最多两周内有效。但是,在这里,我们指定这个token最多四周内有效2,419,200秒)。

    存储在cookie中的token包含用户名、密码、过期时间和一个私钥——在写入cookie前都进行了MD5哈希。默认情况下,私钥的名为SpringSecured,但在这里我们将其设置为spitterKey,使它专门用于Spittr应用。

    如此简单。既然Remember-me功能已经启用,我们需要有一种方式来让用户表明他们希望应用程序能够记住他们。为了实现这一点,登录请求必须包含一个名为remember-me的参数。在登录表单中,增加一个简单复选框就可以完成这件事情:

    <input id="remember_me" name="remember-me" type="checkbox"/>
    <label for="remember_me" class="inline">Remember me</label>
    

    在应用中,与登录同等重要的功能就是退出。如果你启用Remember-me功能的话,更是如此,否则的话,用户将永远登录在这个系统中。我们下面将看一下如何添加退 出功能。

    9.4.4 退出

    其实,按照我们的配置,退出功能已经启用了,不需要再做其他的配置了。我们需要的只是一个使用该功能的链接。退出功能是通过Servlet容器中的Filter实现的(默认情况下),这个Filter会拦截针对“/logout”的请求。

    因此,为应用添加退出功能只需添加如下的链接即可(如下以Thymeleaf代码片段的形式进行了展现):

    <a th:href="@{/logout}">Logout</a>
    

    当用户点击这个链接的时候,会发起对“/logout”的请求,这个请求会被Spring Security的LogoutFilter所处理。用户会退出应用,所有的Remember-me token都会被清除掉。

    在退出完成后,用户浏览器将会重定向到“/login?logout”,从而允许用户进行再次登录。

    如果你希望用户被重定向到其他的页面,如应用的首页,那么可以在configure()中进行如下的配置

    httpSecurity.formLogin()
     .loginPage("/login")
     .and()
     .logout()
     .logoutSuccessUrl("/")
    

    logout()提供了配置退出行为的方法。在本例中,调用logoutSuccessUrl()表明在退出成功之后,浏览器需要重定 向到“/”。

    除了logoutSuccessUrl()方法以外,你可能还希望重写默认的LogoutFilter拦截路径。我们可以通过调用logoutUrl()方法实现这一功能:

    .logout()
    .logoutSuccessUrl("/")
    .logoutUrl("/signout")
    

    如何在发起请求的时候保护Web应用。这假设安全性主要涉及阻止用户访问没有权限的URL。

    但是,如果我们能够不给用户显示其无权访问的连接,那么这也是一个很好的思路。

    9.5 保护视图

    当为浏览器渲染HTML内容时,你可能希望视图中能够反映安全限制和相关的信息。一个简单的样例就是渲染用户的基本信息(比如显示“您已经以……身份登录”)。或者你想根据用户被授予了什么权限,有条件地渲染特定的视图元素。 在第6章,我们看到了在Spring MVC应用中渲染视图的两个最重要的可选方案:JSP和Thymeleaf。不管你使用哪种方案,都有办法在视图上实现安全性。Spring Security本身提供了一个JSP标签库,而Thymeleaf通过特定的方言实现了与Spring Security的集成。 让我们看一下如何将Spring Security用到视图中,就从Spring Security的JSP标签库开始吧。

    9.5.2 使用Thymeleaf的Spring Security方言

    Thymeleaf的安全方言提供了条件化渲染和显示认证细节的能力。

    为了使用安全方言,我们需要确保Thymeleaf Extras Spring Security已经位于应用的类路径下。 然后,还需要在配置中使用SpringTemplateEngine来注册SpringSecurityDialect。

    程序清单9.10所展现的@Bean方法声明了SpringTemplateEngine bean,其中就包含了SpringSecurityDialect。

    @Bean
    public SpringTemplateEngine templateEngine(TemplateResolver templateResolver) {
     SpringTemplateEngine templateEngine = new SpringTemplateEngine();
     templateEngine.setTemplateResolver(templateResolver);
     // 注册安全方言
     templateEngine.addDialect(new SpringStandardDialect());
     return templateEngine;
    }
    

    安全方言注册完成之后,我们就可以在Thymeleaf模板中使用它的属性了。首先,需要在使用这些属性的模板中声明安全命名空间:

    <html xmlns="http://www.w3.org/1999/xhtml"
     xmlns:th="http://www.thymeleaf.org"
    xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
     ...
    </html>
    

    标准的Thymeleaf方法依旧与之前一样,使用th前缀,安全方言则设置为使用sec前缀。

    这样我们就能在任意合适的地方使用Thymeleaf属性了。比如,假设我们想要为认证用户渲染“Hello”文本。如下的Thymeleaf模板代码片段就能完成这项任务:

    <div sec:authorize="isAuthenticated()">
     Hello <span sec:authentication="name">someone</span>
    </div>
    

    好像没有效果。。。。

    9.6 小结

    对于许多应用而言,安全性都是非常重要的切面。Spring Security提供了一种简单、灵活且强大的机制来保护我们的应用程序。

    借助于一系列Servlet Filter,Spring Security能够控制对Web资源的访问,包括Spring MVC控制器。借助于Spring Security的Java配置模型,我们不必直接处理Filter,能够非常简洁地声明Web安全性功能。

    当认证用户时,Spring Security提供了多种选项。我们探讨了如何基于内存用户库、关系型数据库和LDAP目录服务器来配置认证功能。如果这些可选方案无法满足认证需求的话,我们还学习了如何创建和配置自定义的用户服务。

    附:参考内容:

    1. 手工配置springboot + spring security + thymeleaf + thymeleaf-extras-springsecurity https://my.oschina.net/kitos/blog/1632381

    2. Spring-Security自定义登录页&inMemoryAuthentication验证 https://www.cnblogs.com/MrSi/p/7993875.html>

    3. springboot 构建 security https://docs.spring.io/spring-security/site/docs/5.0.13.BUILD-SNAPSHOT/reference/htmlsingle/

    4. 初识 Spring Security https://www.w3cschool.cn/springsecurity/

    5. 参考项目 springboot整合 https://github.com/wean2016/springsecurity

    6. 记住账号 https://www.jianshu.com/p/6537deeac03d

    7. JWT的Java使用 https://blog.csdn.net/qq_37636695/article/details/79265711

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.

    TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

    =================================================================

    实战代码


    基础的自定义页面认证

    import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
    ​
    /**
     * @description:  继承AbstractSecurityWebApplicationInitializer会自动注册DelegatingFilterProxy
     *  等价于xml配置 springSecurityFilterChain
     * @version: 1.0
     * @data: 2019-04-19 11:52
     */
    public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
    }
    
    package com.web.spittr.config.security;
    ​
    import com.web.spittr.data.SpittleRepository;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    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.annotation.web.servlet.configuration.EnableWebMvcSecurity;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.crypto.password.NoOpPasswordEncoder;
    import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
    import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
    ​
    import javax.sql.DataSource;
    ​
    ​
    @Configuration
    @EnableWebMvcSecurity
    //@EnableWebSecurity
    @Slf4j
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ​
     @Autowired
     SpittleRepository spittleRepository;
     @Override
     protected void configure(HttpSecurity httpSecurity) throws Exception {
     // 启用默认的登录页
     httpSecurity
     .formLogin()
     .loginPage("/login")
     .defaultSuccessUrl("/spittle/")
     .failureUrl("/spittle/login?error=true")
     .and()
     .logout()
     .logoutSuccessUrl("/login")
     .logoutUrl("/spittle/login?logout=true")
     .and()
     .rememberMe()
     .tokenValiditySeconds(2439800)
     .key("spittrKey")
     .and()
     .authorizeRequests()
     .antMatchers("/spittle/user/*").hasRole("USER")
     .antMatchers("/spittle/admin/*").hasRole("ADMIN")
     .anyRequest()
     .permitAll()
     ;
     httpSecurity.csrf().disable();
     }
    ​
    ​
     /**
     *
     * @param auth
     * @throws Exception
     */
     @Override
     protected void configure(AuthenticationManagerBuilder auth) throws AuthenticationException {
     System.out.println("加载Security。。。读取权限");
     // 启用内存用户储存
     auth.inMemoryAuthentication()
     .passwordEncoder(NoOpPasswordEncoder.getInstance())
     .withUser("user").password("1").roles("USER").and()
     .withUser("admin").password("1").roles("USER","ADMIN");
     }
    ​
    ​
    ​
    ​
    }
    
    <form method="post" action="/login" >
     <table>
     <tr>
     <td>User:</td>
     <td> <input name="username" type="text" value="" /> </td>
     </tr>
     <tr>
     <td>Password:</td>
     <td> <input name="password" type="password" /> </td>
     </tr>
    ​
     <tr>
     <td><input id="remember_me" name="remember-me" type="checkbox"/></td>
     <td><label for="remember_me" class="inline">Remember me</label></td>
     </tr>
     <input type="hidden"  name="${_csrf.parameterName}"   value="${_csrf.token}"/>
     <tr>
     <td colspan="2">
     <input name="submit" type="submit" value="Login" />
     <input name="reset" type="reset" value="Reset" />
     </td>
     </tr>
     </table>
    ​
    </form>
    

    未完待续

    基础的认证虽然完成了,但是没有从数据读取user信息,需要继续来验证

    相关文章

      网友评论

        本文标题:Spring Security_2

        本文链接:https://www.haomeiwen.com/subject/rpuagqtx.html