一.导入依赖

<!-- Spring Boot Security:处理认证与授权 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

 注意:需要使用SpringBoot web的基础依赖以及测试依赖,防止启动工程时出错.

添加完依赖之后即可启动工程,启动工程后,会在控制台看到如下信息:

Using generated security password: 28b90a94-29d3-4a69-a62c-f4309c72f077

Spring Security有默认的登录的账号和密码,密码是随机的,就是上面的信息中后半部分,每次启动项目都会不一样.

然后访问http://localhost/8080,会看到如下信息:

Spring Security默认要求所有的请求都是要经过登录才允许访问,可以使用默认的用户名:user和我们启动项目的时候得到的密码进行登录.

我们在浏览器输入任意地址都可以,会自动跳转到该登录页面.

二.密码算法

Spring Security的依赖项中包括了一种BCrypt算法工具类,BCrypt是一款非常优秀的密码加密工具,适用于对需要储存下来的密码进行加密.

我们可以进行测试:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@SpringBootTest
public class BCryptPasswordEncoderTests {

    private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    @Test
    void testPasswordEncoder(){
        for (int i = 0; i < 10; i++) {
            //原文相同的情况,每次加密都不同
            String password = "123456";
            String encode = passwordEncoder.encode(password);
            System.out.println("原密码:" + password);
            System.out.println("加密后:" + encode);
        }
    }
}

得到的输出结果:

 可以看到,同样的密码,每一次执行的时候形成的密文都不相同.该加密器还有个验证密码是否匹配的方法,可以来测试一下上面的某一个加密后的密码是否与原密码匹配:

@Test
    void testMatches(){
        String rawPassword = "123456";
        String encode = "$2a$10$x1dLYmPn013AceYXZMJdi.nQ0yTW2p11wRiR1xa0/iakne0Qyj8la";
        boolean matches = passwordEncoder.matches(rawPassword, encode);
        System.out.println(matches);
    }

可以看到执行后的结果为:true

三.身份认证之前

首先,我们要认识到Spring Security框架是用来解决用户认证和授权的一个框架.

其次,我们的数据库中要有一下五张表:

其中,ams_login_log是记录用户登录日志的表,可以暂时不管

然后,要使用Spring Security框架来使用数据库中的信息,验证用户身份,需要添加数据库相关依赖:

mysql-connector-java

mybatis-spring-boot-starter

durid-spring-boot-starter

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.21</version>
</dependency>

并且,在application.properties文件或者application.yml文件中添加相关配置:

mybatis:
  mapper-locations: classpath:mapper/*.xml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mall_ams?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource

创建MybatisConfiguration配置类,用于配置DAO层(持久层)

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("cn.tedu.sso.mapper")
public class MyBatisConfiguration {
}

创建编写持久层接口的包mapper和AdminMapper接口:

package cn.tedu.sso.mapper;

import org.springframework.stereotype.Repository;

@Repository
public interface AdminMapper {
}

四.身份认证

1.需要进行身份认证,首先要实现"根据用户名查询此用户的登录信息(应该包含权限信息)"的查询功能,这个查询功能的sql语句大致是:

select aa.id,
       aa.username,
       aa.password,
       aa.is_enable,
       ap.value
from ams_admin aa
         left join ams_admin_role aar on aa.id = aar.admin_id
         left join ams_role_permission arp on arp.role_id = aar.role_id
         left join ams_permission ap on ap.id = arp.permission_id
where aa.username = 'root';

2.创建AdminLoginVO类,用户保存查询到的用户相关信息

为了更快捷地创建实体类以及VO类,我们需要使用lombok依赖:

<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>
import lombok.Data;

import java.io.Serializable;
import java.util.List;

@Data
public class AdminLoginVO implements Serializable {
    private Long id;
    private String username;
    private String password;
    private Integer isEnable;
    private List<String> permissions;
}

3.在AdminMapper接口中添加方法,用于查询用户信息:


@Repository
public interface AdminMapper {
    AdminLoginVO getLoginInfoByUsername(String username);
}

4.在resources包下创建mapper包,存放xml文件,用于与数据库的查询,在xml文件中编写查询sql语句

<?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="cn.tedu.sso.mapper.AdminMapper">
    <select id="getLoginInfoByUsername" resultMap="QuerySimpleResult">
        select
        <include refid="QuerySimpleSql"/>
        from
        ams_admin
        left join ams_admin_role on ams_admin.id = ams_admin_role.admin_id
        left join ams_role_permission on ams_role_permission.role_id = ams_admin_role.role_id
        left join ams_permission on ams_permission.id = ams_role_permission.permission_id
        where ams_admin.username = #{username}
    </select>

    <sql id="QuerySimpleSql">
        ams_admin.id,
        ams_admin.username,
        ams_admin.password,
        ams_admin.is_enable,

        ams_permission.value
    </sql>

    <resultMap id="QuerySimpleResult" type="cn.tedu.sso.pojo.vo.AdminLoginVO">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="is_enable" property="isEnable"/>
        <collection property="permissions" ofType="java.lang.String">
            <!-- 以下配置类似在Java中执行 new String("/pms/product/read") -->
            <constructor>
                <arg column="value"/>
            </constructor>
        </collection>
    </resultMap>
</mapper>

5.实现UserDetailsService接口

Spring Security认证机制中包含:当客户端提交登录后,会自动调用UserDetailsService接口(Spring Security定义的)的实现类对象中的UserDetails loadUserByUsername(String username)方法(根据用户名加载用户数据),得到UserDetails类型的对象,这个对象中至少包含此用户名对应的密码,权限等信息,接下来,Spring Security会自动完成密码的比对,并确定此次用户是否允许登录.

import cn.tedu.sso.mapper.AdminMapper;
import cn.tedu.sso.pojo.vo.AdminLoginVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private AdminMapper adminMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名查询尝试登录的用户信息
        AdminLoginVO result = adminMapper.getLoginInfoByUsername(username);
        if(result == null){
            //根据用户名没有查询到对应的用户信息,抛出异常
            throw new BadCredentialsException("登录失败,用户不存在!");
        }
        //查询匹配的管理员数据,需要将此数据转换为UserDetails类型对象并返回
        UserDetails userDetails = User.builder()
                .username(result.getUsername())
                .password(result.getPassword())
                .accountExpired(false)//用户是否过期
                .accountLocked(false)//用户是否锁定
                .disabled(result.getIsEnable()!=1)//用户是否启用
                .credentialsExpired(false)//用户证书是否过期
                .authorities(result.getPermissions().toArray(new String[]{}))//用户权限
                .build();
        return userDetails;
    }
}

然后,配置密码加密器

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
public class SecurityConfiguration {
    //配置密码加密器
    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

重启项目,可以发现在启动过程中不再出现随机密码,然后访问浏览器,进入登录页,使用管理员用户名密码即可登录.

//TODO

Spring Security实现单点登录2_寒風冷度夜雨的博客-CSDN博客

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐