Spring-Security 简介、入门案例详解、安全框架、权限验证 SSM项目 使用 JavaConfig配置

一、介绍

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

​ Spring Security对Web安全性的支持大量地依赖于Servlet过滤器。这些过滤器拦截进入请求,并且在应用程序处理该请求之前进行某些安全处理。 Spring Security提供有若干个过滤器,它们能够拦截Servlet请求,并将这些请求转给认证和访问决策管理器处理,从而增强安全性。根据自己的需要,可以使用适当的过滤器来保护自己的应用程序。

注:这篇不牵扯原理及大量理论知识,只是一个入门案例,但是完全足够大家理解和写简单的项目。

二、详细步骤

因为这一篇是讲spring-security 就是用spring写的 但是我在这篇文章中 并没有使用xml配置

全文都是使用javaconfig 进行配置的。

1、创建一个maven项目 添加web 框架。

2、导入依赖

  <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>5.3.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>5.3.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.18</version>
        </dependency>
    </dependencies>

3、项目整体结构

4、Spring 容器配置

@Configuration
@ComponentScan(basePackages = "com.itheima.security.springmvc"
,excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value =
Controller.class)})
public class ApplicationConfig {
//在此配置除了Controller的其它bean,比如:数据库链接池、事务管理器、业务bean等。
}

5、servletContext配置

在config包下定义WebConfig.java,它对应s对应于DispatcherServlet配 置

@Configuration  // 相当于springmvc 文件
@EnableWebMvc
@ComponentScan(
        basePackages = "com.wyh",
        includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)})
public class WebConfig implements WebMvcConfigurer {

    // 视图解析器
    @Bean
    public InternalResourceViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/view/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

    // 把根路径跳转到login上
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("redirect:/login");
    }
}

6、加载 Spring容器

在init包下定义Spring容器初始化类SpringApplicationInitializer,此类实现WebApplicationInitializer接口, Spring容器启动时加载WebApplicationInitializer接口的所有实现类 加载 WebSecurityConfig

public class SpringApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    // spring 容器
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{ ApplicationConfig.class , WebSecurityConfig.class};
    }

    //servletContext
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }
    //url-mapping
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

7、Spring Security初始化

Spring Security初始化,这里有两种情况

若当前环境没有使用Spring或Spring MVC,则需要将 WebSecurityConfig(Spring Security配置类) 传入超 类,以确保获取配置,并创建spring context。

相反,若当前环境已经使用spring,我们应该在现有的springContext中注册Spring Security(上一步已经做将 WebSecurityConfig加载至rootcontext),此方法可以什么都不做。 在init包下定义SpringSecurityApplicationInitializer:

public class SpringSecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

        public SpringSecurityApplicationInitializer(){

        }

}

8、默认根路径请求

默认根路径请求 在WebConfig.java中添加默认请求根路径跳转到/login,此url为spring security提供:

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("redirect:/login");
//        registry.addViewController("/login-view").setViewName("login");
    }

8、认证页面

因为这次只是写一个入门案例 页面就没有额外写了 但是怎么设置还是有说滴。

默认认证页面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JfAwesLK-1618576175316)(spring-security.assets/image-20210416191315376.png)]

8.1、定制登录页面

8.1.1、 在config包WebConfig.java中

//默认Url根路径跳转到/login,此url为spring security提供
@Override
public void addViewControllers(ViewControllerRegistry registry) {
	registry.addViewController("/").setViewName("redirect:/login‐view");
	registry.addViewController("/login‐view").setViewName("login");
}

8.1.2、在WebSecurityConfig.java中 增加配置

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable() // 关闭csrf
            	.authorizeRequests()
                .antMatchers("/r/r1").hasAnyAuthority("p1")// 表示:访问/r/r1资源的 url需要拥有p1权限。
                .antMatchers("/r/r2").hasAnyAuthority("p2")
                .antMatchers("/r/**").authenticated()  // 访问/r/下所有请求都需要通过身份认证
                .anyRequest().permitAll() //指定任何人都允许使用URL
                .and()
                .formLogin()  // 支持form表单认证,认证成功后转向/login-success。
                .loginPage("/login‐view")   //1
                .loginProcessingUrl("/login")// 2
                .successForwardUrl("/login-success")  // 自定义登录成功的配置  认证通过后跳转到login-success
            	.permitAll(); //3
    }

1、指定我们自己的登录页,spring security以重定向方式跳转到/login-view

2、指定登录处理的URL,也就是用户名、密码表单提交的目的路径

3、我们必须允许所有用户访问我们的登录页(例如为验证的用户),这个 formLogin().permitAll() 方法允许 任意用户访问基于表单登录的所有的URL。

9、安全配置 WebSecurityConfig

spring security提供了用户名密码登录、退出、会话管理等认证功能,只需要配置即可使用。

在config包下定义WebSecurityConfig,安全配置的内容包括:用户信息、密码编码器、安全拦截机制。

@EnableWebSecurity
public class WebSecurityConfig  extends WebSecurityConfigurerAdapter {

    @Bean //密码编码器、
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override //安全拦截机制。
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/r/r1").hasAnyAuthority("p1") // 访问/r/r1 需要有p1权限 下同
                .antMatchers("/r/r2").hasAnyAuthority("p2")
                .antMatchers("/r/**").authenticated()  // 访问/r/下所有请求都需要通过身份认证
                .anyRequest().permitAll() //指定任何人都允许使用URL
                .and()
                .formLogin()  // 支持form表单认证,认证成功后转向/login-success。
                .successForwardUrl("/login-success");  // 自定义登录成功的配置
    }
}

10、用户类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyUser{
        private Integer id;
        private String username;
        private String password;
}

11、MyUserDetailsService

在MyUserDetailsService()方法中,我们返回了一个UserDetailsService给spring容器,Spring Security会使用它来 获取用户信息

今天是初使用 不写多了…。

@Component
public class MyUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 登录账号
        // 这里是模拟数据库查询
        MyUser myUser = getUserByName(username);
        if (myUser== null) {
            return null;
        }
        //创建userDetails
        UserDetails userDetails =
                User.withUsername(myUser.getUsername()).password(BCrypt.hashpw(myUser.getPassword(),BCrypt.gensalt())).authorities("p1").build();
        // pssword() 使用了密码加密   加密方式在之前讲了。 
        // 数据库里的密码是不会存明文密码 这里也是在模拟。
  		// 因为我使用了密码加密   我登录输入的密码 
        // security 在处理的时候 也会进行加密 然后再比对。
        return userDetails;
    }
    private MyUser getUserByName(String username){
        return new MyUser(1,username,"123456");
    }
}

12、Controller层

测试的请求

@RestController
public class LoginController {
    @RequestMapping(value = "/login-success", produces = "text/plain;charset=utf-8")
    public String loginSuccess(){
        return getUsername()+"登录成功";
    }


    @RequestMapping(value = "/r/r1", produces = "text/plain;charset=utf-8")
    public String r1(){
        return getUsername()+"资源1";
    }
    @RequestMapping(value = "/r/r2", produces = "text/plain;charset=utf-8")
    public String r2(){
        return getUsername()+"资源2";
    }

    // 这个权限是 身份验证通过就可以访问的  不要用户有权限的。
    @RequestMapping(value = "/r/r3", produces = "text/plain;charset=utf-8")
    public String r3(){
        return  getUsername()+"这个用户是没有任何权限  这个请求也只要登录验证就可"+  "    资源3";
    }

    private String getUsername(){
        String username=null;
        //当前通过的用户身份
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Object principal = authentication.getPrincipal();
        if (principal==null){
            username="匿名";
        }
        if(principal instanceof UserDetails){
            UserDetails userDetails=  (UserDetails)principal;
            username = userDetails.getUsername();
        }else{
            username= principal.toString();
        }

        return username;
    }
}

三、测试

1、认证错误

当输入错误的密码的时候 会自动弹出提示。

2、认证成功

当用户名和密码都输入正确的时候 它就会跳转到 /login-success请求去

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ODtp2Cts-1618576175320)(spring-security.assets/image-20210416200732223.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UbeqWyOh-1618576175323)(spring-security.assets/image-20210416200747582.png)]

3、授权

当我们的用户有权限的时候 就是用我现在模拟的数据(username:admin password:123456 p1 权限)

来访问 /r/r1

接下来接着访问 /r/r2 /r/r2 是需要p2权限 模拟的数据是没有这个权限

会报 403 错误 权限不足。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6OTK8bUl-1618576175327)(spring-security.assets/image-20210416201142415.png)]

在 /r/** 下 所有用户都需要身份验证通过才可以访问的。

如果在没有登录的情况下 访问 /r/r3 是会自动转到登录页面去。

自言自语

今天是做了一个security 的入门案例 。

今天比平时写的时间还长一些。

看完的话 最基本的是可以用了 那里面的数据 是可以从数据库里面查询的。

权限表的设计 在我之前一篇博客已经发出来了。

RBAC、控制权限设计、权限表设计

等过几天把springboot-security 的案例也写出来

那个是从数据库查询的,不再是模拟数据拉。

继续。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值