SpringSecurity基于数据库认证以及权限管理

    上一篇对SpringSecurity做了基本介绍,以及做了一个基于内存用户认证的小栗子,在企业开发中,都会连接数据库来做认证,那怎么来基于数据库做认证呢,今天我们就动手练习下,我们使用的环境呢,springboot+springsecurity+mybaits+mysql来搭建。

    没有mysql数据库环境的小伙伴,可以看下我之前的文章,来安装mysql

docker安装mysql

    安装完数据库后,创建一个名为SpringSecurity的schema数据库,导入两个表,一个是认证用的表,一个就是授权用的表

create table sys_user (
  uid int(11) not null
  ,username varchar(12) not null
  ,password varchar(120) not null
  ,status int(1)
  ,rid int(11) not null
);

 

create table sys_role (
  rid int(11) not null
  ,role_name varchar(30)
  ,role_desc varchar(60)
);

    接下来,还是创建一个javaweb的项目,上一篇也有提到,登录spring官网的创建项目页面https://start.spring.io/,添加依赖时,我们选择Lombok,Spring Web,Thymeleaf,Spring Security,MyBatis Framework,MySQL Driver等,如果还有自己常用的依赖的话,添加进去,完成后使用idea或者eclipse导入环境,确认pom文件,下边的依赖被加上。

    <dependencies>
        <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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </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>

    </dependencies>

        我们使用mybatis来操作数据库,我们来编写mapper文件,这里我们就用注解的方式,来写sql文。

  • UserMapper.java

package com.example.demo.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import com.example.demo.domain.SysUser;

public interface UserMapper {

    @Select("select * from sys_user where username = #{username}")
    @Results({
            @Result(id = true, property = "uid", column = "uid"),
            @Result(property = "roles", column = "uid", javaType = List.class, many = @Many(select = "com.example.demo.mapper.RoleMapper.findByUid"))
    })
    public SysUser findByName(String username);
}

  • RoleMapper .java

package com.example.demo.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Select;

import com.example.demo.domain.SysRole;

public interface RoleMapper {

    @Select("select r.rid rid, r.role_name roleName, r.role_desc roleDesc from sys_role r, sys_user u   where u.uid = #{uid} and r.rid = u.rid")
    public List<SysRole> findByUid(Integer uid);
}

    mapper文件就是做两个事情,一个是验证用户名和密码,在一个就是,校验成功后,取得该用户权限,下边我们编写service,由于SpringSecurity为我们提供了用户验证的UserDetailsService接口,我们就来创建自己的service来继承并实现它,添加我们自己的业务处理。

  • UserService.java

package com.example.demo.service;

import org.springframework.security.core.userdetails.UserDetailsService;

public interface UserService extends UserDetailsService {

}

  • UserServiceImpl .java

package com.example.demo.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userMapper.findByName(username);
    }

}
 

    这里代码不是很复杂,就是调用下我们已经写好的mapper文件,传入用户名,实现数据库验证,我们继续往下说,来介绍下SpringSecurity的配置类。

  • SecurityConfig.java

package com.example.demo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Bean;
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 com.example.demo.service.UserService;

@Configurable
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    private UserService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //        auth.inMemoryAuthentication().withUser("user")
        //                .password("{noop}123") // {noop}
        //                .roles("USER");
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/login.html", "/css/**", "/js/**").permitAll().antMatchers("/**")
                .hasAnyRole("USER", "ADMIN", "PRODUCT").anyRequest().authenticated()
                .and().formLogin().loginPage("/login.html")
                .loginProcessingUrl("/login").successForwardUrl("/index")
                .failureForwardUrl("/failer.html").permitAll().and().logout().logoutUrl("/logout")
                .logoutSuccessUrl("/login.html").invalidateHttpSession(true).permitAll().and().csrf().disable();
    }

}

    代码中有注释的部分,就是我们上篇文章提到的,将用户名和密码保存到内存中的认证方式,实现数据库验证呢,就是将我们自己写好的service赋值给SpringSecurity,设定service的同时,由于密码保存的是密文,也要设定一种加密的方式,在内部做校验,SpringSecurity框架做的很强大,帮我们做了很多事情。

    再说说授权的事情,SpringSecurity为我们提供了@EnableGlobalMethodSecurity(securedEnabled = true)注解,这个注解类中有三种认证方式,项目中只能开启一种,开启两种的话,配置权限只会一个管用

  • prePostEnabled

        可以写SpringEL表达式的权限管理方式,例如:@PreAuthorize("hasAnyRole('user')")

  • securedEnabled

        SpringSecurity默认的权限管理方式,例如:@Secured({ "ROLE_user", "ROLE_admin" })

  • jsr250Enabled

        支持jdk层面的注解

    这里我开启了SpringSecurity默认的权限管理注解,它是怎么使用的呢,我们就创建一个Controller类,用户只有ROLE_PRODUCT的权限时才能访问。

package com.example.demo.controller;

import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class ProductController {

    @Secured("ROLE_PRODUCT")
    @RequestMapping("/products")
    public String products() {
        return "products";
    }

}

    这里还有一点需要注意的一点是,数据库中保存的密码都是密文,由于是测试程序,我们需要自己手动写一个程序,来将明文密码转换为密文密码,保存到数据库中。

package com.example.demo.config;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class Test {

    public static void main(String[] args) {

        System.err.println(new BCryptPasswordEncoder().encode("123"));

    }

}

输出的结果

$2a$10$vNZ2pvTCM84Ajx8WgIh8B.oYpaRgqGrST.UCHS3jxNwcB/MT7N6Ui

    到这里,SpringSecurity的数据库认证以及授权的方法就介绍完了,在每一个项目中,都会涉及到认证和授权,并且也不单单只是本文介绍的内容,小伙伴们多查资料,了解了SpringSecurity会省去我们很多自己写的代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值