SpringSecurity的使用

1、springsecurity是什么

SpringSecutiy是咋们的Spring团队给我们开发的这样一个基于认证和授权的框架
简单的说 就是原来项目的认证(登陆) 和 授权是程序员自己编写代码来完成的
​
认证:用户的身份认证(登陆)
     就是用户登陆成功之后 每一次访问其他的资源 这个时候 我们也需要做身份判断
     
授权:判断用户是否具有访问某一个方法的权利
​
也就是说我们在学习了 SpringSecurity之后 这个登陆  认证  授权的 功能就不能自己开发了 我们的SpringSecurity就帮助我们自动的实现了
​
SpringSecurity实际上 在开发过程中对 前后不分离的项目 支持的比较好  对我们前后分离的项目 支持的没有那么好  简单的说如果你的项目是前后分离的话 使用了SpringSecurity的话 那么还需要自定义很多的类 去完成我们的功能

2、SpringSecurity能干什么

用户登陆
​
用户身份认证
​
用户授权(鉴权)
​
​
常见的认证和授权的框架有哪些?
​
shiro    springsecurity   这两个框架的功能是一样的 那么这两个之间有啥区别呢?
​
shiro支持在任何场景下使用 而且轻量级
​
springsecurity必须在Spring依赖的场景下使用
​
从使用场景来说 shiro 更加广泛的使用场景 springsecurity要相对弱一些
​
那么你们肯定有一个疑问?
​
那么为什么我们要学习springsecurity呢?
​
shiro:这个是一个团队开发的  sprignsecurity是spring这个团队开发的
​
如果是你要使用shiro的话 那么所有的配置都需要自己去写 简单的说 springboot中是没有shiro的自动配置的 但是security就不一样了 所有的配置 springboot都帮你完成了....
​
从学习的成本上说 shiro也更小  但是可能在spring的场景下 你要使用可能就更加麻烦一些  而且现在市场上的趋势就是使用security
​

3、SpringSecurity的基本使用

3.1、导包

  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
​
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
​
​
        <!--生成getter和setter方法的地方-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

3.2、编写控制器代码

@RestController
public class UserController {
​
    @RequestMapping("/test")
    public Object test(){
        return "你最帅";
    }
​
}

3.3、编写启动类

@SpringBootApplication
public class Application {
    public static void main(String[] args){
        SpringApplication.run(Application.class,args);
    }
}
​

3.4、编写配置类

@SpringBootConfiguration
@ComponentScan(basePackages = {"com.zuo"})
public class AppConfig {
}
​

3.5、测试

在浏览器上输入 http://127.0.0.1:8080/test

 

3.6、用户名和密码

 

 

4、SpringSecurity的运行原理是什么

SpringSecurity的底层原理 就是一些列的 过滤器
​
也就是说 只要你访问资源 那么就会执行到我们指定的过滤器中去  然后在过滤器中去完成用户需要的功能
​
我们说了 过滤器是为了规避Java中的 单一职责
​
那么什么是单一职责呢?
​
简单的说就是自己的事情 自己做  不要将所有的业务耦合到 一个业务中去  过滤器的底层是一个 责任链的设计模式
​
那么在SpringSecurity中有三个过滤器比较重要
​
FilterSecurityInterceptor:
这个过滤器的作用是一个方法级别的过滤器、这个过滤器位于责任链的最底层(简单的说 就是最后一个执行的过滤器)、这个过滤器的作用就是判断用户是否具有访问某一个方法的权限
​
ExceptionTranslationFilter:
这个过滤器的作用是:用来进行异常处理的这样一个过滤器。这个过滤器的作用是捕获认证或者授权产生的异常的、简单的说这个过滤器的作用是用来进行异常处理的
​
UsernamePasswordAuthenticationFilter:
这个过滤器的作用:用户身份认证的这样一个过滤器 简单的说就是进行用户的身份认证的、判断用户身份是否合法的
​
那么这个过滤器是怎么运行起来的呢?看我上图:

 

 

 

 

 

 

5、SpringSecurity在Web下的使用

5.1、设置用户名和密码的第一种方式

 

5.2、设置用户名和密码的第二种方式

package com.zuo.config;
​
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
​

@SpringBootConfiguration
@Slf4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {
​
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //在这里你可以设置自己的用户名和密码
        //  现在波波想问你们个问题:密码你们2阶段项目存储到数据库是怎么存储的?
        /**
         * MD5?是不是加密?
         *    如果你们认为MD5是加密那么 你解密下?
         * MD5不是加密:这个是一种散列算法  他还有一个名字叫做 文档摘要
         * 他的设计初衷是为了验证文件在传输过程中 是否被人篡改过....
         *
         * 散列算法有一个特点:就是单向的 只能散列 不能还原 他为什么不能还原?
         *
         * 是因为他在进行散列的时候并不是对 这个字符串的每一位 都进行了散列 而是对这个字符串的某些位置进行了散列
         * 最终求了一个散列的结果
         *
         *    xiaobobotiedangousheng    0  2   5  7  9
         *
         *    uhajdhasdjkhvfnfeifv
         * 随意他不能还原
         *
         * 你们有没有想过一个问题:两个不同的字符传是不是取出来散列的位置恰好一样? 这个是有可能的
         * 这个就称为 MD5的碰撞
         *
         * MD5碰撞如何解决呢?
         *    1、增长字符串 减少出现碰撞的概率
         *    2、在进行MD5散列之前 先进行SHA1散列 再进行MD5散列 减少MD5碰撞的概率
         *
         *    上面的问题说明一个原因 密码存储到数据库 不能是明文
         *
         */
        //我们要继续你给MD5的散列
​
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        String encode = bCryptPasswordEncoder.encode("123");
        log.info("密码散列之后的值是:"+encode);
        auth.inMemoryAuthentication().withUser("tiedan").password(encode).roles("agent");
    }
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
}
​

5.3、设置用户名和密码的第三种方式

5.3.1、编写查询数据库的类

@Component
public class MyUserDetailService implements UserDetailsService {
    /**
     * @param username:这个username实际上是前端页面传递过来的这个用户名
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //拿到用户名 通过用户名去数据库查询用户对象?
        //TODO 这个里面应该编写查询数据库的代码
        if(!"xiaobobo".equals(username)){
           throw new UsernameNotFoundException("用户名不对");
        }
        //程序执行到这里 说明用户名是对的 那么就应该返回 UserDetails的对象
​
        //模拟从数据库查询出来了密码
​
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        String encode = bCryptPasswordEncoder.encode("123");
​
        //这个权限逻辑意义上是通过 用户id查询出来的用户权限或者角色
        List<GrantedAuthority> authorityList = AuthorityUtils.createAuthorityList("ROLE_admin", "ROLE_aunt", "user:query");
​
        User user = new User("xiaobobo",encode,authorityList);
        return user;
    }
}

5.3.2、编写配置文件

@SpringBootConfiguration
public class SecurityConfig1 extends WebSecurityConfigurerAdapter {
​
    @Autowired
    private MyUserDetailService myUserDetailService;
​
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.userDetailsService(myUserDetailService);
        super.configure(http);
    }
​
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
}
​

6、自定义登陆页面

6.1、编写登陆页面

   <form method="post" action="/login">
       用户名:<input type="text" name="username"><br/>
       密码:<input type="password" name="password"> <br/>
       <input type="submit" value="登陆">
   </form>

6.2、编写配置文件

package com.zuo.config;
​
import com.zuo.service.impl.MyUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
​
@SpringBootConfiguration
public class SecurityConfig1 extends WebSecurityConfigurerAdapter {
​
    @Autowired
    private MyUserDetailService myUserDetailService;
​
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.userDetailsService(myUserDetailService);
​
        http.formLogin()
                .loginPage("/login.html")  //这个是设置登陆页面的地址
                .successForwardUrl("/test") //登陆之后的跳转地址
                .loginProcessingUrl("/login") //这个随便设置的(登陆的表单提交地址)
                .failureForwardUrl("/login.html") //登陆失败往login.html跳转
                .usernameParameter("username")   //表单中用户名的名字是什么
                .passwordParameter("password");  //表单中密码的名字是啥
​
        //还要进行页面的拦截设置
        http.authorizeRequests()
                .antMatchers("/login.html").permitAll() //这个页面不需要登陆就能访问
                .anyRequest().authenticated();  //这个表示的是其他的任何请求都要认证之后才能访问
​
        http.csrf().disable();  //关闭我们的csrf校验
​
        super.configure(http);
    }
​
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

7、看下源码

 

 

 

 

 

 

 

 

 

 

 

 

 

 

8、用户退出功能的实现

 

8.1、编写退出页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
​
  <a href="/logout">退出</a>
​
</body>
</html>

8.2、编写配置文件

package com.zuo.config;
​
import com.zuo.service.impl.MyUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
​
@SpringBootConfiguration
public class SecurityConfig1 extends WebSecurityConfigurerAdapter {
    @Autowired
    private MyUserDetailService myUserDetailService;
​
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.userDetailsService(myUserDetailService);
​
        http.logout()
                .logoutUrl("/logout")   //这个就是用户退出登陆的这个地址
                .logoutSuccessUrl("/login.html");  //推迟登陆成功之后跳转的这个页面
​
        http.formLogin()
                .loginPage("/login.html")  //这个是设置登陆页面的地址
                .successForwardUrl("/test") //登陆之后的跳转地址
                .loginProcessingUrl("/login") //这个随便设置的(登陆的表单提交地址)
                .failureForwardUrl("/login.html") //登陆失败往login.html跳转
                .usernameParameter("username")   //表单中用户名的名字是什么
                .passwordParameter("password");  //表单中密码的名字是啥
​
        //还要进行页面的拦截设置
        http.authorizeRequests()
                .antMatchers("/login.html").permitAll() //这个页面不需要登陆就能访问
                .anyRequest().authenticated();  //这个表示的是其他的任何请求都要认证之后才能访问
​
        http.csrf().disable();  //关闭我们的csrf校验
​
        super.configure(http);
    }
​
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
}
​

9、基于角色和权限的配置

9.1、使用代码的方式实现角色和权限的控制

9.1.1、编写控制器方法

   /**
     * 测试基于权限的控制(配置文件实现该功能)
     * @return
     */
    @RequestMapping("/test1")
    public Object test1(){
        return "你是最帅的";
    }
    /**
     * 测试基于角色的控制(配置文件实现该功能)
     * @return
     */
    @RequestMapping("/test2")
    public Object test2(){
        return "真奇怪";
    }

9.1.2、编写配置

.antMatchers("/test1").hasAuthority("user:delete") //这个表示的是test1的这个地址需要 user:delete这个权限才能访问
                .antMatchers("/test2").hasRole("teacher")
                //具有其中一个角色就能访问
//                .antMatchers("/test2").hasAnyRole()
                //具有任意的一个权限就能访问
//                .antMatchers("/test2").hasAnyAuthority()
​
                .anyRequest().authenticated();  //这个表示的是其他的任何请求都要认证之后才能访问

9.1.3、编写友好页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
​
  <h2>大哥你没有权限访问这个地址</h2>
​
</body>
</html>

9.2、使用注解的形式实现方法的角色和权限的控制

9.2.1、开启我们security的注解功能

@EnableGlobalMethodSecurity(securedEnabled=true,prePostEnabled = true)

9.2.2、编写控制器的方法

    /**
     * 判断用户是否具有某一个角色 然后来访问这个资源
     * @return
     */
    @Secured({"ROLE_teacher","ROLE_admin"}) //一个成立就成立
    @RequestMapping("/test5")
    public Object test5(){
        System.out.println("执行了这个代码----");
        return "yes";
    }

10、记住我的这个功能的实现

10.1、打开这个记住我的这个功能

//打开记住我的这个功能
        http.rememberMe().userDetailsService(myUserDetailService)
                .tokenRepository(persistentTokenRepository())
                .tokenValiditySeconds(2592000);

10.2、导包

 <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>
​
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
​
​
        <!--这个包就是mybatis-plus的包-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0.5</version>
        </dependency>
​
​
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.40</version>
        </dependency>
​
​
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>

10.3、编写项目的全局配置

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql:///xxx

10.4、创建数据库和表

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;

10.5、在登陆页面添加记住我的按钮

​
  
 <form method="post" action="/login">
       用户名:<input type="text" name="username"><br/>
       密码:<input type="text" name="password"> <br/>
       记住我<input type="checkbox" name="remember-me"><br/>
       <input type="submit" value="登陆">
   </form>

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值