SpringBoot学习(六)SpringSecurity学习

来源:互联网 发布:淘宝网皮衣加厚的 编辑:程序博客网 时间:2024/06/11 15:49

SpringSecurity介绍

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

应用级别的安全主要分为“验证( authentication) ”和“(授权) authorization ”两个部分。这也是Spring Security主要需要处理的两个部分。“ Authentication ”指的是建立规则( principal )的过程。规则可以是一个用户、设备、或者其他可以在我们的应用中执行某种操作的其他系统。” Authorization “指的是判断某个 principal 在我们的应用是否允许执行某个操作。在 进行授权判断之前,要求其所要使用到的规则必须在验证过程中已经建立好了。

除了验证机制, Spring Security 也提供了一系列的授权能力。主要感兴趣的是以下三个方面:

  1. 对web请求进行授权
  2. 授权某个方法是否可以被调用
  3. 授权访问单个领域对象实例

关于SpringSecurity的学习主要是参考http://www.tianshouzhi.com/api/tutorials/spring_security_4/250,这里面还提供了对源码的解读,对于我这种渣渣来说,看起来还是有点费劲的

在Springsecurity中有两种配置方式,是xml文件配置和java配置,两者配置差别不打,个人感觉java配置使用起来比较方便,所以主要记录一下java配置的过程。

java基本配置

新建项目

新建一个SpringBoot项目,pom.xml中需要的包(勾选或者直接手写)

devtoolsthymeleafsecurity

在pom中添加这三个包之后,可以启动项目看看,访问项目下的任意一个路径都会被拦截要求输入用户名和密码,由于啥都还没有设置,所以用户名是user,密码在启动springboot的时候在console输出了,类似f1ea0b2d-b053-40fd-abc5-065a4f45b266这样的一串就是默认密码。

这里写图片描述

这里写图片描述

配置自己的用户名密码

在项目根目录下添加SecurityConfig.java文件

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter{    @Autowired    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {        auth            .inMemoryAuthentication()                .withUser("user").password("password").roles("USER");    }}

然后访问任意一个URL都会跳转到下面这个界面

这里写图片描述

输入刚刚设置的用户名密码就可以重新跳转回所要访问的界面

这段代码内容很少,但事实上已经做了很多的默认安全验证,包括:

  1. 访问应用中的每个URL都需要进行验证
  2. 生成一个登陆表单
  3. 允许用户使用username和password来登陆
  4. 允许用户注销
  5. CSRF攻击拦截
  6. Session Fixation攻击
  7. 安全Header集成

配置自定义的登录界面

SecurityConfig.java中添加下面的代码,主要是说拦截所有请求,然后配置自定义的登陆界面的路径为“/login”

@Overrideprotected void configure(HttpSecurity http) throws Exception {       http            .authorizeRequests()                  .anyRequest().authenticated()                  .and()            .formLogin()                  .loginPage( "/login")                  .permitAll();      }

在templates文件夹下添加login.html文件,我这个html文件是从之前写的直接拷贝过来的。

<html xmlns:th="http://www.thymeleaf.org"><head><meta content="text/html;charset=utf-8" /><title>登录界面</title><style type="text/css">.starter-template {    padding: 40px 15px;    text-align: center;}</style></head><body>    <div class="container">        <div class="starter-template">            <p th:if="${param.logout}" class="bg-warning">已成功注销</p>            <p th:if="${param.error}" class="bg-danger">有错误,请重试</p>            <h2>使用账号密码登陆</h2>            <form name="form" th:action="@{/login}"  method="POST">                <div class="form-group">                    <label for="username">账号:</label>                     <input type="text" class="form-control" name="username" value="" placeholder="账号" />                </div>                <div class="form-group">                    <label for="password">密码:</label>                     <input type="password" class="form-control" name="password" placeholder="密码" />                </div>                <input type="submit" id="login" class="btn btn-primary" />            </form>        </div>    </div></body></html>

然后在添加一个文件WebMvcConfig,将“/login”映射到“login.html”

import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;@Configurationpublic class WebMvcConfig extends WebMvcConfigurerAdapter{    @Override    public void addViewControllers(ViewControllerRegistry registry) {        registry.addViewController("/login").setViewName("login");    }}

这个时候重新访问刚刚的路径,就会拦截到自定义的登录界面

这里写图片描述

这里Html文件中的用户名密码,默认的name属性名字是“username”和“password”,否则springsecurity是获取不到值的,如需要自定的话,可以这么设置

@Overrideprotected void configure(HttpSecurity http) throws Exception {       http            .authorizeRequests()                  .anyRequest().authenticated()                  .and()             .formLogin()                  .loginPage( "/login")                  .usernameParameter("username")                  .passwordParameter("password")                  .permitAll();}

退出登录

可以在configure函数下继续设置:

@Overrideprotected void configure(HttpSecurity http) throws Exception {       http            .authorizeRequests()                  .anyRequest().authenticated()                  .and()             .formLogin()                  .loginPage( "/login")                  .usernameParameter("username")                  .passwordParameter("password")                  .permitAll()                  .and()             .logout()                 .logoutUrl("/logout")                                                                  .logoutSuccessUrl("/index")                                                            .invalidateHttpSession(true)                  .permitAll();} 

可以直接从代码中看出来,就是访问“/logout”的时候会退出登录,若成功退出便会访问到“/index”这个路径,这个比较简单,就不贴代码出来了,可以下载我的源码去看看。

拦截url和授权

之前设置的都是拦截全部的URL,不管什么URL都拦截到登录界面,也可以自定义拦截,同样是在configure函数下添加设置:

  • resources路径下的文件和以show开头的路径无需登录结课访问
  • test该URl只能由用户为ADMIN的访问
  • index该路径可由ADMIN和USER_ADMIN的用户访问
@Overridepublic  void configure(HttpSecurity http) throws Exception {       http        .authorizeRequests()                                                                      .antMatchers( "/resources/**", "/show*").permitAll()          .antMatchers( "/test").hasRole("ADMIN" )                              .antMatchers( "/idnex").access("hasRole('ADMIN') and hasRole('USER_ADMIN')")          .antMatchers( "/AddUser").access("hasRole('ADMIN') and hasRole('USER')")  //          .regexMatchers("").permitAll()正则表达式匹配        .anyRequest().authenticated()       //             .authorizeRequests()//                   .anyRequest().authenticated()                  .and()            .formLogin()                  .loginPage( "/login")                  .usernameParameter("username")                  .passwordParameter("password")                  .permitAll()                  .and()            .logout()             .logoutUrl("/logout")                                                              .logoutSuccessUrl("/index")                                                        .invalidateHttpSession(true)              .permitAll();      }

关于用户的权限,可在设置用户名和密码是指定

@Autowired    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {        auth            .inMemoryAuthentication()                .withUser("1").password("1").roles("ADMIN").and()                .withUser("2").password("2").roles("USER").and()                .withUser("3").password("3").roles("USER_ADMIN").and()                .withUser("4").password("4").roles("USER");    }  

配置mysql数据库中的用户名密码

关于数据库的而配置连接,参考我的上一篇博客http://blog.csdn.net/q15150676766/article/details/76795466,这里不做介绍。

我数据库中一张表SysUser(id,username,password,role)

在java中新建一个entity类SysUser.java注意这个类需要实现UserDetails接口。数据库中的role字段需要有”ROLE_”作为前缀,或者需要在构建的时候拼接“ROLE_”,我这里是拼接的。

@Entitypublic class SysUser implements UserDetails{    /**     *      */    private static final long serialVersionUID = 1L;    @Id    @GeneratedValue    private Long id;    private String username;    private String password;    private String role;    //getter....setter....    @Override    public Collection<? extends GrantedAuthority> getAuthorities() {        List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();        auths.add(new SimpleGrantedAuthority("ROLE_"+role));        return auths;    }    @Override    public boolean isAccountNonExpired() {        // TODO Auto-generated method stub        return true;    }    @Override    public boolean isAccountNonLocked() {        // TODO Auto-generated method stub        return true;    }    @Override    public boolean isCredentialsNonExpired() {        // TODO Auto-generated method stub        return true;    }    @Override    public boolean isEnabled() {        // TODO Auto-generated method stub        return true;    }}

新建一个JPA的类SysUserRepository.java这个很简单就不做解释了。

import org.springframework.data.jpa.repository.JpaRepository;public interface SysUserRepository extends JpaRepository<SysUser, Long>{    SysUser findByUsername(String username);}

新建一个CustomUserService.java文件

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;public class CustomUserService implements UserDetailsService {    @Autowired    SysUserRepository userRepository;    @Override    public UserDetails loadUserByUsername(String username) {        SysUser user = userRepository.findByUsername(username);        if (user == null) {            throw new UsernameNotFoundException("用户不存在");        }        return user;    }}

最后在SecurityConfig.java文件中添加

    @Bean    UserDetailsService customUserService(){        return new CustomUserService();    }

在configure方法下添加

auth.userDetailsService(customUserService());

这样就可以将数据库中的用户名密码添加到springsecurity中。
关于原理可以去参考一下http://www.tianshouzhi.com/api/tutorials/spring_security_4/250

测试这里便不做了,可以下载我的源码去试试看