zoukankan      html  css  js  c++  java
  • 基于数据库的认证

    在 WebSecurityConfig 添加@EnableGlobalMethodSecurity 注解开启方法的访问权限, 代码如下:
    @EnableWebSecurity //是 Spring Security 用于启用 Web 安全的注解 @EnableGlobalMethodSecurity(prePostEnabled = true) // 开启方法级安全验证 
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 
             //... 
    }
    代码解释:
    prePostEnabled=true 会解锁@PreAuthorize 和@PostAuthorize 两个注解, 
    @PreAuthorize 注解会在方法执行前进行验证,而@PostAuthorize 注解在方法执行后进行验证。
    在控制层添加访问接口
    在 UserController 类中增加方法的访问权限:
    @RestController 
    public class UserController { 
                @Autowired 
                private UserInfoService userInfoService; 
                @GetMapping("/getUser") 
                public UserInfo getUser(@RequestParam String username){ 
                            return userInfoService.getUserInfo(username); 
    }
                @PreAuthorize("hasAnyRole('user')") // 只能 user 角色才能访问该方法 
                @GetMapping("/user") 
                public String user(){ 
                            return "hello,user"; 
                }
                @PreAuthorize("hasAnyRole('admin')") // 只能 admin 角色才能访问该方法 
                @GetMapping("/admin") 
                public String admin(){ 
                            return "hello,admin"; 
                } 
    }
    代码解释:
    @PreAuthorize("hasAnyRole('user')")注解表示访问该方法需要 USER 角色。
    密码加密保存
           上文中的用户密码都是手动在数据库添加的,所以数据库中是明文显示,在实际开发中, 都是需要加密保存的。下面模拟注册用户,加密保存密码:
    修改 mapper 接口
    在 UserMapper 接口中添加插入用户,代码如下:
    @Mapper 
    @Repository 
    public interface UserMapper { 
                   //... 
                    @Insert("insert into user(username, password, role) value(#{username}, #{password}, #{role})") 
                    int insertUserInfo(UserInfo userInfo); 
    }
    修改 service 类
    在 UserInfoService 类中添加插入方法,并且密码要加密保护,代码如下:
    
    @Service 
    public class UserInfoService { 
                // ... 
                 @Autowired 
                 private PasswordEncoder passwordEncoder; 
                // ... 
                 public int insertUser(UserInfo userInfo){ 
                     /* 加密密码*/ 
    userInfo.setPassword(passwordEncoder.encode(userInfo.getPassword())); 
                         return userMapper.insertUserInfo(userInfo); 
          } 
    }
    修改 controoler
    
    在 UserController 类中添加插入用户接口,代码如下:
    @RestController 
    public class UserController{ 
          //... 
             @PostMapping("/addUser") 
           public int addUser(@RequestBody UserInfo userInfo){ 
               return userInfoService.insertUser(userInfo); 
           } 
    }
    添加失败,响应的状态码显示 401 Unauthorized, 说明无权限,需要登录,但注册用户是不用登录的,所以需要给注册用户释放权限。 
           修改 WebSecurityConfig 配置类,重写 configure(HttpSecurity http)方法,配置允许注册用户的请求访问:
    @EnableWebSecurity 
    @EnableGlobalMethodSecurity(prePostEnabled = true) 
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 
        //... 
           @Override 
           protected void configure(HttpSecurity http) throws Exception { 
                  http
                          .authorizeRequests() 
    // 允许 post 请求/addUser,而无需认证 
                          .antMatchers(HttpMethod.POST, "/addUser").permitAll() 
                          .anyRequest().authenticated() // 所有请求都需要验证
                          .and() 
                          .formLogin() // 使用默认的登录页面 
                          .and() 
                          .csrf().disable(); 
    // post 请求要关闭 csrf 验证,不然访问报错;实际开发中开启,需要前端配合传递其他参数 
    }}
    使用加密密码登录
    
           使用加密密码登录,需要修改 CustomUserDetailsService 类,之前从数据库拿到明文密码后需要加密,现在数据库里面的密码已经加密了,就不用加密了,代码如下:
    @Component 
    public class MyUserDatailService implements UserDetailsService { 
               //... 
               @Override 
               public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 
               // ... 
               return new User( 
                          userInfo.getUsername(), 
                          // 数据库密码已加密,不用再加密 
                          userInfo.getPassword(), 
                          authorities 
               ); 
         } 
    }
           角色继承实际上是一个很常见的需求,因为大部分公司治理可能都是金字塔形的,上司 可能具备下属的部分甚至所有权限,这一现实场景,反映到我们的代码中,就是角色继承了。 Spring Security 中为开发者提供了相关的角色继承解决方案,只需要开发者在配置类中提供 一个 RoleHierarchy 即可,代码如下:
    @Bean 
    RoleHierarchy roleHierarchy() { 
          RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); 
          String hierarchy = "ROLE_dba > ROLE_admin \n ROLE_admin > ROLE_user"; 
          roleHierarchy.setHierarchy(hierarchy); 
          return roleHierarchy; 
    }
          在这里我们提供了一个 RoleHierarchy 接口的实例,使用字符串来描述了角色之间的继承关系, ROLE_dba 具备 ROLE_admin 的所有权限,而 ROLE_admin 则具备 ROLE_user的所有权限。提供了这个 Bean 之后,以后所有具备 ROLE_user 角色才能访问的资源,ROLE_dba 和 ROLE_admin 也都能访问,具备 ROLE_amdin 角色才能访问的资源,ROLE_dba 也能访问。
    <project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.dsecurity</groupId>
        <artifactId>dsecurity</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.0.0.RELEASE</version>
            <relativePath /> <!-- lookup parent from repository -->
        </parent>
    
        <properties>
            <!-- 声明项目配置依赖编码格式为 utf-8 -->
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <fastjson.version>1.2.24</fastjson.version>
        </properties>
    
        <dependencies>
    
            <!--Spring Data Jpa依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            <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.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.3</version>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
                <version>8.0.13</version><!--$NO-MVN-MAN-VER$ -->
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <optional>true</optional>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </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>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    server.port =8089
    
    spring.datasource.url=jdbc:mysql://localhost:3306/springbootdb?serverTimezone=UTC&autoReconnect=true
    spring.datasource.username=root
    spring.datasource.password=admin
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    
    logging.level.com.example.bdatabaserole.mapper=debug
    
    mybatis.mapper-locations=classpath:mappers/*.xml
    mybatis.type-aliases-package=com.tszr.mapper
    mybatis.configuration.map-underscore-to-camel-case=true
    
    spring.jpa.database=MySQL
    spring.jpa.show-sql=true
    spring.jpa.hibernate.ddl-auto=create-drop
    spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
    <?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">
    <!--命名空间必须和UserMapper全类名相同 -->
    <mapper namespace="com.tszr.mapper.UserMapper">
        <select id="getUserByUsername"
            resultType="com.tszr.entity.User">
            select * from user where username = #{username};
        </select>
        <select id="getRolesById" resultType="com.tszr.entity.Role">
            select * from role where id in(select rid from user_role where uid=#{uid});
        </select>
    </mapper>
    package com.tszr.entity;
    
    import javax.persistence.CascadeType;
    import javax.persistence.Column;
    import javax.persistence.JoinColumn;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.ManyToOne;
    import javax.persistence.Table;
    
    @Entity
    @Table(name = "role")
    public class Role {
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getNameZh() {
            return nameZh;
        }
    
        public void setNameZh(String nameZh) {
            this.nameZh = nameZh;
        }
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @ManyToOne(cascade=CascadeType. REFRESH ,optional= false)
        @ JoinColumn (name =  "user_id" )
        private int id;
        
        @Column(name = "name", unique = true, nullable = false, length = 64)
        private String name;
            
        @Column(name = "nameZh", nullable = false, length = 64)
        private String nameZh;
    }
    package com.tszr.entity;
    
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    
    import com.fasterxml.jackson.annotation.JsonIgnore;
    
    import java.io.Serializable;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    @Entity
    @Table(name = "user")
    public class User implements Serializable, UserDetails {
        private static final long serialVersionUID = 1L;
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private int id;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public List<Role> getRoleList() {
            return roleList;
        }
    
        public void setRoleList(List<Role> roleList) {
            this.roleList = roleList;
        }
    
        @Column(name = "username", unique = true, nullable = false, length = 64)
        private String username;
    
        @Column(name = "password", nullable = false, length = 64)
        private String password;
        
        @JsonIgnore
        private List<Role> roleList;
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            Collection<GrantedAuthority> authorities = new ArrayList<>();
            for (Role role : roleList) {
                // 数据库role表字段中是以ROLE_开头的,所以此处不必再加ROLE_
                authorities.add(new SimpleGrantedAuthority(role.getName()));
            }
            return authorities;
        }
    
        /**
         * 指示用户的账户是否已过期。无法验证过期的账户。 如果用户的账户有效(即未过期),则返回true,如果不在有效就返回false
         */
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
    
        /**
         * 指示用户是锁定还是解锁。无法对锁定的用户进行身份验证。 如果用户未被锁定,则返回true,否则返回false
         */
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
    
        /**
         * 指示用户的凭证(密码)是否已过期。过期的凭证阻止身份验证 如果用户的凭证有效(即未过期),则返回true 如果不在有效(即过期),则返回false
         */
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
        /**
         * 指示用户是启用还是禁用。无法对禁用的用户进行身份验证 如果启用了用户,则返回true,否则返回false
         */
        @Override
        public boolean isEnabled() {
            return true;
        }
    }
    package com.tszr.mapper;
    
    import java.util.List;
    
    import org.apache.ibatis.annotations.Insert;
    import org.apache.ibatis.annotations.Mapper;
    import org.springframework.stereotype.Repository;
    
    import com.tszr.entity.Role;
    import com.tszr.entity.User;
    
    @Mapper
    @Repository
    public interface UserMapper {
    //  @Select("select * from user where username = #{username}")
        User getUserByUsername(String username);
    
        List<Role> getRolesById(int id);
    
        // 添加用户
        @Insert("insert into user(username, password) value(#{username}, #{password})")
        int insertUserInfo(User user);
    }
    package com.tszr.services;
    
    import org.springframework.stereotype.Service;
    
    import com.tszr.entity.User;
    import com.tszr.mapper.UserMapper;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    @Service
    public class UserInfoService {
        @Autowired
        private UserMapper userMapper;
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        public int insertUser(User userInfo) {
            // 加密密码
            userInfo.setPassword(passwordEncoder.encode(userInfo.getPassword()));
            return userMapper.insertUserInfo(userInfo);
        }
    
        public User getUserInfo(String username) {
            return userMapper.getUserByUsername(username);
        }
    }
    package com.tszr.services;
    
    import com.tszr.entity.User;
    import com.tszr.mapper.UserMapper;
    
    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 CustomUserDetailsService implements UserDetailsService {
        @Autowired
        private UserInfoService userInfoService;
        @Autowired
        private UserMapper userMapper;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            // 通过用户名从数据库获取用户信息
            User userInfo = userInfoService.getUserInfo(username);
            if (userInfo == null) {
                throw new UsernameNotFoundException("用户不存在");
            }
            userInfo.setRoleList(userMapper.getRolesById(userInfo.getId()));
            return userInfo;
        }
    }
    package com.tszr.controller;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.access.prepost.PreAuthorize;
    
    import com.tszr.entity.User;
    import com.tszr.services.UserInfoService;
    
    @RestController
    public class UserController {
        @Autowired
        private UserInfoService userInfoService;
    
        // 添加
        @PostMapping("/addUser")
        public int addUser(@RequestBody User userInfo) {
            return userInfoService.insertUser(userInfo);
        }
    
        @GetMapping("/getUser")
        public User getUser(@RequestParam String username) {
            return userInfoService.getUserInfo(username);
        }
    
        @PreAuthorize("hasAnyRole('user')") // 只能user角色才能访问该方法
        @GetMapping("/user")
        public String user() {
            return "hello,user";
        }
    
        @PreAuthorize("hasAnyRole('dba','admin')") // dba\admin角色可以访问该方法
        @GetMapping("/db")
        public String dba() {
            return "hello,dba,admin";
        }
    
        @PreAuthorize("hasAnyRole('admin')") // 只能admin角色才能访问该方法
        @GetMapping("/admin")
        public String admin() {
            return "hello,admin";
        }
    }
    package com.tszr.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.http.HttpMethod;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    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.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    import com.tszr.services.CustomUserDetailsService;
    
    @EnableWebSecurity // 是Spring Security用于启用Web安全的注解
    @EnableGlobalMethodSecurity(prePostEnabled = true) // 开启方法级安全验证
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        private CustomUserDetailsService userDatailService;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests().antMatchers(HttpMethod.POST, "/addUser").permitAll() // 允许post请求/add-user,而无需认证
                    .anyRequest().authenticated() // 所有请求都需要验证
                    .and().formLogin() // 使用默认的登录页面
                    .and().csrf().disable();// post请求要关闭csrf验证,不然访问报错;实际开发中开启,需要前端配合传递其他参数
        }
    
        /**
         * 指定加密方式
         */
        @Bean
        public PasswordEncoder passwordEncoder() {
            // 使用BCrypt加密密码
            return new BCryptPasswordEncoder();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            // 从数据库读取的用户进行身份认证
            auth.userDetailsService(userDatailService).passwordEncoder(passwordEncoder());
        }
    
    }
    package com.tszr;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
  • 相关阅读:
    转载:quartz详解:quartz由浅入深
    git提交忽略文件或文件夹
    Spring学习笔记(一)
    转载:RabbitMQ常用命令
    linux安装rabbitMQ
    linux安装redis
    springMVC+spring+mybatis多数据源配置
    (二)RabbitMQ使用笔记
    ASP.NET Core 异常处理与日志记录
    ASP.NET Core中间件实现分布式 Session
  • 原文地址:https://www.cnblogs.com/tszr/p/15455628.html
Copyright © 2011-2022 走看看