Spring Security:userDetailsService用户认证以及授权

编写:Horin

文章目录

  • 前言
  • 一、导入框架依赖
  • 二、操作流程
    • 1.继承WebSecurityConfigurerAdapter并重写方法
    • 2.实现UserDetailsService接口
    • 3.编写数据持久化接口
    • 4.创建数据库
    • 5.Mapper映射文件
    • 6.编写实体类
    • 7.完善SecurityConfig配置:授权、自定义页面等
    • 8.登录页面修改为form表单提交
  • 总结


前言

Spring 安全性是提供认证、授权和防范常见攻击的框架。由于对imperative和reactive应用程序的安全都提供了第一类支持,因此它是保护基于 Spring 的应用程序的事实上的标准。

有关功能的完整列表,请参见引用的Features部分。
Spring安全 中文文档


提示:以下是本篇文章正文内容,下面案例可供参考

一、导入框架依赖

1、添加依赖:pom.xml


<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.6.7version>
        <relativePath/> 

    parent>

    <groupId>com.examplegroupId>
    <artifactId>TheTokenartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>TheTokenname>
    <description>TheTokendescription>
    <properties>
        <java.version>1.8java.version>
    properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
        
        <dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
            <version>2.2.0version>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-jdbcartifactId>
        dependency>
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-thymeleafartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-securityartifactId>
        dependency>
        
        <dependency>
            <groupId>org.thymeleaf.extrasgroupId>
            <artifactId>thymeleaf-extras-springsecurity5artifactId>
        dependency>
        
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
                <version>2.6.7version>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombokgroupId>
                            <artifactId>lombokartifactId>
                        exclude>
                    excludes>
                configuration>
            plugin>
        plugins>
    build>

project>

二、操作流程

1.继承WebSecurityConfigurerAdapter并重写方法

配置类:SecurityConfig.java

package com.example.thetoken.config;

import com.example.thetoken.service.serviceImpl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.AccessDeniedException;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;


@EnableWebSecurity  // 开启MVC security安全支持
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private DataSource dataSource;
    
    /**
     * userDetailsService身份验证
     */
    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    /**
     * 用户授权管理自定义配置
     * @param http
     * @throws Exception
     */

    /**
     * 用户身份认证自定义配置
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
        /**
         * 明文加密
         */
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        
        /**
         * 1、内存认证
         */
//        auth.inMemoryAuthentication().passwordEncoder(encoder)
//                .withUser("jsor").password(encoder.encode("12312333")).roles("common")
//                .and()
//                .withUser("wanglaoji").password(encoder.encode("123456")).roles("admin");

        /**
         * 2、UserDetailsService认证
         */
        auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
    }

}

2.实现UserDetailsService接口

实现类:UserDetailsServiceImpl.java

package com.example.thetoken.service.serviceImpl;

import com.example.thetoken.domain.User;
import com.example.thetoken.domain.Authority;
import com.example.thetoken.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @Classname UserDetailsServiceImpl
 * @Description 自定义一个UserDetailsService接口实现类进行用户认证信息封装
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        /**
         * 查询用户信息
         * selectUser():通过用户名查询用户密码
         * selectAuths():查询该用户权限
         */
        User user = userMapper.selectUser(s);
        List<Authority> authorities = userMapper.selectAuths(s);

        /**
         * 封装用户权限
         */
        List<SimpleGrantedAuthority> list = authorities.stream().map(
                authority -> new SimpleGrantedAuthority(authority.getAuthority())).collect(Collectors.toList());

        /**
         * 将对应值传入security的User对象
         */
        if(user!=null){
            System.out.println(user.getUserName()+"  "+user.getUserAccount()+"  "+user.getUserPassword());
            String userA = user.getUserAccount();
            String pasA = user.getUserPassword();
            System.out.println(list);
            //数据库为明文密码,所以使用对象来转换为需要的加密格式
            BCryptPasswordEncoder bCryptPasswordEncoder=new BCryptPasswordEncoder();
            UserDetails userDetails=
                    new org.springframework.security.core.userdetails.User(userA, bCryptPasswordEncoder.encode(pasA), list);
            System.out.println(userDetails);
            return userDetails;
        } else {

            /**
             * 不存在用户抛出为空
             */
            return null;
        }
    }
}

3.编写数据持久化接口

实现类:UserMapper.java

package com.example.thetoken.mapper;


import com.example.thetoken.domain.Authority;
import com.example.thetoken.domain.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

import java.util.List;


@Mapper
public interface UserMapper {
	
	//验证用户账号是否匹配
    User selectUser(String userAccount);
    
    //获取该用户名的权限
    List<Authority> selectAuths(String userAccount);

}

4.创建数据库

SQL导出文件:springbootf.sql

/*
 Navicat MySQL Data Transfer

 Source Server         : 127.0.0.1
 Source Server Type    : MySQL
 Source Server Version : 50723
 Source Host           : localhost:3306
 Source Schema         : springbootf

 Target Server Type    : MySQL
 Target Server Version : 50723
 File Encoding         : 65001

 Date: 01/06/2022 19:19:38
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for authority
-- ----------------------------
DROP TABLE IF EXISTS `authority`;
CREATE TABLE `authority`  (
  `id` int(50) NOT NULL,
  `authority` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of authority
-- ----------------------------
INSERT INTO `authority` VALUES (1, 'ROLE_admin');
INSERT INTO `authority` VALUES (2, 'ROLE_common');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `user_id` int(5) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `user_account` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `user_password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `sex` int(1) NOT NULL COMMENT '0-女,1-男',
  PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, '江治雨', '8823', 'j12312333', 1);
INSERT INTO `user` VALUES (2, '周杰伦', '123', '123', 1);
INSERT INTO `user` VALUES (4, '邓丽君', 'dengli312884', '987654321', 0);
INSERT INTO `user` VALUES (6, '张国荣', '401', 'zgr', 1);
INSERT INTO `user` VALUES (7, 's', 's', '', 1);

-- ----------------------------
-- Table structure for user_authority
-- ----------------------------
DROP TABLE IF EXISTS `user_authority`;
CREATE TABLE `user_authority`  (
  `id` int(20) NOT NULL,
  `user_id` int(50) NOT NULL,
  `authority_id` int(50) NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user_authority
-- ----------------------------
INSERT INTO `user_authority` VALUES (1, 1, 1);
INSERT INTO `user_authority` VALUES (2, 2, 2);

SET FOREIGN_KEY_CHECKS = 1;


5.Mapper映射文件

映射文件:UserMapper.xml


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.thetoken.mapper.UserMapper">

    <resultMap id="BaseResultMap" type="com.example.thetoken.domain.User">
        <id property="userId" column="user_id"/>
        <result property="userName" column="user_name"/>
        <result property="userAccount" column="user_account"/>
        <result property="userPassword" column="user_password"/>
        <result property="sex" column="sex"/>
    resultMap>
    
    <select id="selectUser" parameterType="string" resultMap="BaseResultMap">
        select * from user where user_account = #{userAccount}
    select>

    <select id="selectAuths" parameterType="string" resultType="Authority">
        select a.* from user c, authority a, user_authority ca
                   where
                       ca.user_id=c.user_id and ca.authority_id=a.id and c.user_account =#{userAccount}
    select>
    
mapper>


6.编写实体类

省略:User实体类、Authority实体类


7.完善SecurityConfig配置:授权、自定义页面等

配置类:SecurityConfig.java

package com.example.thetoken.config;

import com.example.thetoken.service.serviceImpl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.AccessDeniedException;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;


@EnableWebSecurity  // 开启MVC security安全支持
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private DataSource dataSource;
    /**
     * 第三种身份验证
     */
    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    /**
     * 用户授权管理自定义配置
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        super.configure(http);
        /**
         * 自定义授权
         */
        http.authorizeRequests()
                // 静态资源、登录、注册页面放行
               .antMatchers("/login/**","/moban/**","/toLogin","/toZhuce").permitAll()
//                .antMatchers("/templates/**").hasRole("common")
                //管理员可以访问用户列表(接口list)
                .antMatchers("/list").hasRole("admin")
                .anyRequest().authenticated();

        /**
         * 自定义登录
         */
        http.formLogin()
                //跳转登陆页面接口
                .loginPage("/toLogin")
                //以下Controller接口无需编写
                .loginProcessingUrl("/loginss")
                //主页面接口
                .defaultSuccessUrl("/");

        /**
         * 记住我
         */
        http.rememberMe()
                .rememberMeParameter("rememberme")
                .tokenValiditySeconds(2000);
                // 对cookie信息进行持久化管理
               // .tokenRepository(tokenRepository());

        /**
         * 自定义无权限跳转页面
         */
        http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
            @Override
            public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
                // 如果是权限访问异常,则进行拦截到指定错误页面
                RequestDispatcher dispatcher = httpServletRequest.getRequestDispatcher("/err");
                dispatcher.forward(httpServletRequest, httpServletResponse);
            }
        });
    }
    /**
     * 持久化Token存储
     * @return
     */
    @Bean
    public JdbcTokenRepositoryImpl tokenRepository(){
        JdbcTokenRepositoryImpl jr=new JdbcTokenRepositoryImpl();
        jr.setDataSource(dataSource);
        jr.setCreateTableOnStartup(false);
        return jr;
    }

    /**
     * 用户身份认证自定义配置
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        /**
         * 明文加密
         */
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        /**
         * 1、内存认证
         */
//        auth.inMemoryAuthentication().passwordEncoder(encoder)
//                .withUser("jsor").password(encoder.encode("12312333")).roles("common")
//                .and()
//                .withUser("wanglaoji").password(encoder.encode("123456")).roles("admin");

        /**
         * 2、UserDetailsService认证
         */
        auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
    }

}

8.登录页面修改为form表单提交

登录页面:index.html

注意事项:仅供参考结构,未上传css样式文件。
重点一,input的name属性必须固定,账号为username,密码为password。
重点二,form标签的action与配置类中的登录接口一样,spring会自动创建。

DOCTYPE html>
<html  xmlns:th="http://www.thymeleaf.org">
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>欢迎登录title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  
  <link th:href="@{/login/css/style.css}" rel="stylesheet">
  
  <link th:href="@{https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700&display=swap}" rel="stylesheet">
  <script th:src="@{/login/js/jquery-3.1.1.min.js}">script>
  <script th:src="@{/login/js/jquery.min.js}">script>
head>

<body>
<script th:if="${userList!=null}"> alert("账号或者密码输入错误!");script>

<div class="main">
  <div class="logo text-center">
    <h1> <a href="index.html">叭叭贰叁™ 进销系统a>h1>
  div>

  <div class="content-w3ls text-center" >
      <form th:action="@{/loginss}" th:method="post">
      
      <div class="wthree-field">
        <input name="username" type="text"  placeholder="用户名" />
      div>

      
      <div class="wthree-field">
        <input name="password"  type="password" placeholder="密码" />
      div>

      <div class="wthree-field">
        <button  class="btn"  type="submit" >登录button>
      div>

      <div class="login-bottom">
        <a th:href="@{/toZhuce}" class="">没有账户?创建一个。a>
      div>

      form>
  div>


  <div class="copyright">
    <p>© 2021 Universe Signin Form. Made with music | Designed by <a
            href="" target="_blank">Horina>p>
  div>
div>

body>

html>

总结

其中HTML页面、Controller跳转补充完成,就可以正常运行了。

你可能感兴趣的:(java,spring,boot,安全)