第一篇SpringSecurity的初次邂逅

Posted 波波烤鸭

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第一篇SpringSecurity的初次邂逅相关的知识,希望对你有一定的参考价值。

SpringSecurity的初次邂逅

一、SpringSecurity介绍

官网说明:

  Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.
  Springsecurity 是一个强大的、高度可定制的身份验证和访问控制框架。它是确保基于 spring 的应用程序安全的事实标准。
  Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements
  Springsecurity 是一个框架,它关注于为 Java 应用程序提供身份验证和授权。像所有 Spring 项目一样,Spring Security 的真正强大之处在于它可以很容易地扩展以满足定制需求

特征

  • 对身份验证和授权的全面且可扩展的支持
  • 保护免受会话固定,点击劫持,跨站点请求伪造等攻击
  • Servlet API集成
  • 与Spring Web MVC的可选集成

二、SpringSecurity入门案例

  我们通过一个SpringBoot项目来集成SpringSecurity的应用。

1.创建项目并添加依赖

  我们先创建一个SpringBoot项目,然后添加如下的依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

2.启动项目

  然后我们就可以启动项目了,项目启动会帮我们创建一个账号为 user 密码在控制台输出的登录信息。

在浏览器中提交对应的请求会直接调回到默认的登录页面。

  然后可以通过上面讲的user账号来登录,当然SpringSecurity已经帮助我们实现了认证的逻辑,如果密码输错也是登录不了的.

登录成功后就能直接请求我们需要的资源了。

报出404的原因是我们在服务端并没有index.html文件。

3.自定义登录页面

  上面的登录页面使用的是SpringSecurity中默认提供的,接下来我们需要自定义一个登录页面。前端模板通过Thymeleaf来实现,对应的添加依赖文件

        <!-- 添加thymeleaf的依赖文件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

  然后创建login.html页面文件,并放在template目录下。页面效果

  页面中的代码见附件,现在系统使用的还是默认的登录页面,我们需要替换掉,这时我们需要创建SpringSecurity的配置类.


/**
 * SpringSecurity的配置文件
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter 

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.authorizeRequests()
                // 配置需要放过的资源
                .antMatchers("/login.html","/css/**","/img/**")
                .permitAll()
                .antMatchers("/**")
                .hasAnyRole("USER")
                .anyRequest()
                .authenticated()
                .and()
                // 配置登录表单相关的信息
                .formLogin()
                // 指定自定义的登录页面
                .loginPage("/login.html")
                .permitAll();
    

  login.html是在template目录下的,我们没有办法直接访问,所以我们还需要定义一个controller来实现调整,对应的代码为:

@Controller
public class WebController 

    @GetMapping(value = "/login.html")
    public String loginPage()
        return "login";
    

  然后我们就可以重启服务来访问了。

4.自定义登录的账号

  现在登录用的账号密码是系统生成,如果我们需要使用自己的呢,我们可以在自定义的配置类中来指定

     @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        // 通过内存来存储账号和密码
        auth.inMemoryAuthentication()
                .withUser("zhang")
                .password("noop123") // noop非加密的方式
                .roles("USER");
    

然后我们就可以用自定义的账号密码来登录了

5.数据库认证

  然后我们系统和关系型数据库中的用户表做认证。我们先创建对应的表结构,然后整合MyBatis来实现。

DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `salt` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
)

整合MyBatis的步骤自行完成,不赘述。

  在数据库中创建了一条测试用户 admin admin

然后创建对应的SysUser对象

@Data
public class SysUser 

    private Integer id;

    private String userName;

    private String password;

    private String salt;
  

创建对应的UserService接口,注意这个接口必须要继承UserDetailService。

/**
 * 用户的Service
 */
public interface UserService extends UserDetailsService 


然后创建对应的接口实现类,在实现类中重写 loadUserByusername方法来完成根据账号查询的方法

/**
 * UserService接口的实现类
 */
@Service
public class UserServiceImpl implements UserService 

    @Autowired
    UserMapper userMapper;

    /**
     * 根据账号密码验证的方法
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 
        SysUser user = userMapper.queryByUserName(username);
        System.out.println("---------->"+user);
        if(user != null)
            // 账号对应的权限
            List<SimpleGrantedAuthority> authorities = new ArrayList<>();
            authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
            // 说明账号存在 noop 非加密的使用
            UserDetails details = new User(user.getUserName(),"noop"+user.getPassword(),authorities);
            return details;
        
        throw new UsernameNotFoundException("账号不存在...");

    

  注意在SpringSecurity中的每个用户都需要和对应的权限绑定,所以我们在上面直接给查询的用户赋予了 USER的权限,之前在配置资源的时候其实也都要求上了。

注意:置的角色名自动加上 ‘ ROLE_ ’ 前缀。所以在数据库中将角色名设置为带 ‘ ROLE_ ’ 前缀的值便能成功访问限制的地址。

  最后修改下配置文件中的认证方式,从原来的从内存中获取认证到自定义Service认证。

  然后登录测试即可

6.加密处理

  密码的加密处理肯定是必不可少的,在SpringSecurity中推荐使用BCryptPasswordEncoder来加密,动态加盐的方式。

    public static void main(String[] args) 
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String password = "admin";
        // 每次都会生成一个随机的salt,同一个明文加密多次编码得到的密文其实都不一样
        System.out.println(encoder.encode(password));
        System.out.println(encoder.encode(password));
        System.out.println(encoder.encode(password));
    

输出的信息

$2a$10$YvUoUNCvkLrokXbJkPGbH.508ho1ePHDIK5vDYWWdGiDcYwxMML6y
$2a$10$mLfDJSd0md9R28pncRVeTOtabMNOzfcIUnGHt.r/VrgAXTwm06mBO
$2a$10$owOqhXJ5kHOV7jFcERQPjuckUbmuimEfF6DqJOXskURvtqC6.NNZ6

然后在项目中我们保存生成的密文在数据库中

然后把UserService中的认证方法的逻辑修改

然后在配置文件中关联对应的加密处理

  登录测试,成功既搞定~

7.认证状态

  我们在实际项目中会因为用户的不同操作可能会出现不同的状态,比如:正常,冻结,过期等,在SpringSecurity中也是支持的。我们来看看,具体的如何来实现的。

修改认证的代码设置账号为过期的状态

登录测试效果

8.记住我

  接下来看看怎么实现RememberMe功能,首先我们需要在表单中添加 记住我 的勾选按钮

然后还得放开 记住我 的功能,默认是关闭的

然后测试:先正常登录

然后我们关闭浏览器,再登录,发现不需要登录可以访问了

  当然现在的情况是把remember-me的信息记录在内存中的,如果要实现持久化那么我们需要创建对应的表结构来记录。以下是状态持久化对应的表结构。

CREATE TABLE `persistent_logins` (
 `username` varchar(64) NOT NULL,
 `series` varchar(64) NOT NULL,
 `token` varchar(64) NOT NULL,
 `last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE 
CURRENT_TIMESTAMP,
 PRIMARY KEY (`series`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

其实在JdbcTokenRepositoryImpl中帮我们定义的相关的表结构

  同时我们需要调整对应的配置文件中的配置信息

    /**
     * 向Spring容器中注入 PersistentTokenRepository 对象
     * @param dataSource
     * @return
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository(DataSource dataSource)
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        return tokenRepository;
    

    @Autowired
    private PersistentTokenRepository persistentTokenRepository;


最后在配置文件中关联

然后测试:登录成功可以看到返回的信息

同时去数据库中查看

然后关闭浏览器重新访问,发现是可以的,搞定

当你删除掉数据库中的记录后,再访问就需要登录了

以上是关于第一篇SpringSecurity的初次邂逅的主要内容,如果未能解决你的问题,请参考以下文章

我和Memcached初次邂逅

初次接受C语言游戏程序感受

当推荐系统邂逅深度学习

和Android的第一次美丽邂逅

我的第一篇博客

U-BOOT-2016.07移植 (第一篇) 初步分析