ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

8-2 spring-security学习

2021-08-02 19:59:43  阅读:100  来源: 互联网

标签:String spring springframework 学习 org import security public


Spring -Security学习

本质上是一个过滤链
SpringSecurity主要包括认证和授权两大功能。
认证:确实是否登录
授权:限制访问权限

认证授权注解使用

1、@Secured

使用方法:在启动类添加 开启此注解使用

添加注解

@EnableGlobalMethodSecurity(securedEnabled = true)

2、在controller方法的注解上添加注解。

示例:

    @RequestMapping("/level3/{id}")
    @Secured({"ROLE_ADMIN","ROLE_MANAGER"})
    public String level3(@PathVariable("id") int id){
        return "views/level3/"+id;
    }

2、@PreAuthorize

使用方法:在启动类上添加以下注解:

@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)

PreAuthorize该注解源码注释,值是一个el表达式,在调用之前被调用

public @interface PreAuthorize {

   /**
    * @return the Spring-EL expression to be evaluated before invoking the protected
    * method
    */
   String value();

}

controller中

3、@PostAuthorize

在方法执行之后进行验证,可以进行一些处理

使用方法:在启动类上添加以下注解:

@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rxLKj0bQ-1627905031861)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20210802152437702.png)]

4、@PostFilter

权限验证之后对数据进行过滤

自动登录技术

使用token

实现

1、创建数据表(或者在代码中自动创建)

create table learn.persistent_logins
(
    username  varchar(64)                         not null,
    series    varchar(64)                         not null
        primary key,
    token     varchar(64)                         not null,
    last_used timestamp default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP
);

2、修改配置类

注入数据源,配置操作数据对象

@Autowired
DataSource dataSource;
//配置对象
@Bean
public PersistentTokenRepository persistentTokenRepository(){
    JdbcTokenRepositoryImpl jdbcTokenRepository= new JdbcTokenRepositoryImpl();
    jdbcTokenRepository.setDataSource(dataSource);
    //jdbcTokenRepository.setCreateTableOnStartup(true);  //启动的时候创建一个数据表
    return jdbcTokenRepository;

}

3、配置类中配置自动登录

 @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 定制请求的授权规则
        // 首页所有人可以访问
        http.and()
        .rememberMe()                   //记住我
        .tokenRepository(persistentTokenRepository())   //配置token仓库
        .userDetailsService(userDetailsService)       //配置查询的用户数据服务
        .tokenValiditySeconds(60)                  //配置有效时间,单位秒
;
    }

4、页面添加记住我复选框

<div><input type="checkbox" name="remember-me" title="记住密码"></div>

name属性必须是remember-me

完整项目

1、导入pom依赖

<!--        JDBC-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
<!--      spring security  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>
        <dependency>
            <groupId>p6spy</groupId>
            <artifactId>p6spy</artifactId>
            <version>3.8.0</version>
        </dependency>
        <!--        代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <!--        模板依赖-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-log4j -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j</artifactId>
            <version>1.3.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>compile</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
<!--        thymeleaf-extras-shiro -->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.crazycake/shiro-redis -->
<!--        Shiro redis 缓存-->
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>3.3.1</version>
        </dependency>

application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost/learn?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
  thymeleaf:
    cache: false
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    logic-delete-value: 1 # 逻辑已删除值(默认为 1)
    logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
server:
  port: 8001

sql建表

create table learn.user           #用户表
(
    id       int auto_increment
        primary key,
    name     varchar(20)   null,
    password varchar(20)   null,
    age      int           null,
    gender   int default 0 null,
    role     varchar(20)   null
);

create table learn.persistent_logins #用来存储记住我的选项
(
    username  varchar(64)                         not null,
    series    varchar(64)                         not null
        primary key,
    token     varchar(64)                         not null,
    last_used timestamp default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP
);

配置类:

MybatisPlusConfig

package com.security.Config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.security.mapper")
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }
    //分页插件
    @Bean
    public PaginationInnerInterceptor paginationInnerInterceptor(){return new PaginationInnerInterceptor();}

}

P6SpyLogger用于打印sql查询语句分析


import com.p6spy.engine.spy.appender.MessageFormattingStrategy;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

@Component
public class P6SpyLogger implements MessageFormattingStrategy {

    private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");


    @Override
    public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql, String s4) {
        return !"".equals(sql.trim()) ? this.format.format(new Date()) + " | took " + elapsed + "ms | " + category + " | connection " + connectionId + "\n " + sql + ";" : "";
    }
}

WebMvcConfig

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {

        registry.addViewController("/index").setViewName("index");
        registry.addViewController("/error").setViewName("error");
        registry.addViewController("/403").setViewName("403");
    }
}
SecurityConfig

import com.security.service.UserDetailsServiceImpl;
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.WebSecurityConfiguration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
      UserDetailsServiceImpl userDetailsService;
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 定制请求的授权规则
        // 首页所有人可以访问
        http
                .authorizeRequests().antMatchers("/","/login").permitAll()
               // .antMatchers("/level1/**").hasRole("ADMIN")               //role和authority有什么区别
                //.antMatchers("/level2/**").hasAnyRole("vip1","vip2")
                //.antMatchers("/level3/**").hasAuthority("vip3")
                //.antMatchers("/level3/**").hasAnyAuthority("vip1","vip2")//有任何其中某一个权限就可以访问
                .and()
                .exceptionHandling().accessDeniedPage("/403")   //配置403界面
        .and()
                .logout()                                       //配置推出注销
                .logoutUrl("/logout")                           //注销的url链接
                .logoutSuccessUrl("/index")                     //注销成功的页面跳转
                .permitAll()
        .and()
                .rememberMe()                                   //记住我
                .tokenRepository(persistentTokenRepository())   //配置token仓库
                .userDetailsService(userDetailsService)         //配置查询的用户数据服务
                .tokenValiditySeconds(60)                       //配置有效时间,单位秒
        ;
        //定制请求的授权规则

            //....
            //开启自动配置的注销的功能
            // /logout 注销请求
        http
                .formLogin()
                .loginPage("/dologin")         //登录链接
                .successForwardUrl("/success") //成功的页面转发
                .failureForwardUrl("/error");   //错误的页面转发
    }
    //  定义认证规则
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        //在内存中定义,也可以在jdbc中去拿....
        //在这里在数据库中查询
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Autowired
    DataSource dataSource;
    //配置对象
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository= new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        //jdbcTokenRepository.setCreateTableOnStartup(true);  //启动的时候创建一个数据表
        return jdbcTokenRepository;

    }

}
//pojo实体类
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="User对象", description="")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private Integer id;

    private String name;

    private String password;

    private String role;
}
//UserMapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface UserMapper extends BaseMapper<User> {

}

//================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.security.mapper.UserMapper">

</mapper>

MyUserDetailService
import com.security.mapper.UserMapper;
import com.security.pojo.User;
import com.security.service.impl.USERervice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;

@Service
//重写UserDetailsService的loadUserByUsername方法
public class MyUserDetailService implements UserDetailsService {

  @Autowired(required = false)
    private USERervice useRervice;

  @Autowired
  PasswordEncoder passwordEncoder;


  @Override
  public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
      User user =useRervice.queryByName(name);
      if (user==null){
        return null;
      }else {
        //创建一个权限的集合
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        //添加获取权限
        authorities.add(new SimpleGrantedAuthority(user.getRole()));
        //把对象信息(用户名,密码,权限)存入对象,返回该对象,controller层直接调用

        org.springframework.security.core.userdetails.User user2 =new org.springframework.security.core.userdetails.User(user.getName(), passwordEncoder.encode(user.getPassword()), authorities);

        System.out.println("管理员信息:"+user.getName()+"   "+passwordEncoder.encode(user.getPassword())+"  "+user2.getAuthorities());
        return user2;
      }

  }
}

controller

import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class BaseController {



    @RequestMapping({"/","/index","index.html"})
    public String index(){
        return "index";
    }

    @RequestMapping("/login.html")
    public String LoginHtml(){
        return "login";
    }

    @RequestMapping("/loginout")
    public String loginout(){

        return "login";
    }
    @RequestMapping("/level1/{id}")
    @PostAuthorize("hasAuthority('MANAGER')")
    public String level1(@PathVariable("id") int id){
        System.out.println("已处理》》》》》》》》》》》》》》》》");      //此处执行
        return "views/level1/"+id;                     //不返回,无权限
    }

    @RequestMapping("/level2/{id}")
    @Secured({"ROLE_VIP2","ROLE_EMPLOYEE"})
    public String level2(@PathVariable("id") int id){
        return "views/level2/"+id;
    }

    @RequestMapping("/level3/{id}")
    @PreAuthorize("hasRole('ROLE_VIP3')")
    public String level3(@PathVariable("id") int id){
        return "views/level3/"+id;
    }
        @PostMapping("/success")
    public String toLogin(){
            return "redirect:index";
    }
}

html

index.html

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

<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body align="center">
<div> <a th:href="@{/login.html}">登陆界面</a></div>
<div > <a th:href="@{/loginout}">注销</a></div>
<hr/>
<div >
    <div><a th:href="@{/level1/1}">level1-view1</a> </div>
    <div><a th:href="@{/level1/2}">level1-view2</a> </div>
    <div><a th:href="@{/level1/3}">level1-view3</a> </div>
</div>
<div>
    <div><a th:href="@{/level2/1}">level2-view1</a> </div>
    <div><a th:href="@{/level2/2}">level2-view2</a> </div>
    <div><a th:href="@{/level2/3}">level2-view3</a> </div>
</div>
<div>
    <div><a th:href="@{/level3/1}">level3-view1</a> </div>
    <div><a th:href="@{/level3/2}">level3-view2</a> </div>
    <div><a th:href="@{/level3/3}">level3-view3</a> </div>
</div>
</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body align="center">
<form th:action="@{/dologin}" method="post">
 <div><input placeholder="用户名" type="text" name="username"></div>
  <div><input placeholder="密码" type="text" name="password"></div>
    <div><input type="checkbox" name="remember-me" title="记住密码"></div>
  <div><input type="submit" ></div>
</form>
</body>
</html>

error.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body align="center">
<h1>错误</h1>
</body>
</html>

我的疑惑:hasRole与hasAuthority有什么区别?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vt5aG6ce-1627905031858)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20210802144202247.png)]

hasRole

hasAnyRole

hasAuthority

hasAnyAuthority

点开源码:

hasRole

private static String hasRole(String role) {
   Assert.notNull(role, "role cannot be null");
   Assert.isTrue(!role.startsWith("ROLE_"),
         () -> "role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'");
   return "hasRole('ROLE_" + role + "')";
}

hasAuthority

private static String hasAuthority(String authority) {
   return "hasAuthority('" + authority + "')";
}

就是说:role会自动加前缀 ROLE_ ,所以在数据库中,role字段的前面也必须加ROLE_

标签:String,spring,springframework,学习,org,import,security,public
来源: https://blog.csdn.net/qq_43876243/article/details/119332666

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有