Spring Boot 整合SpringSecurity 实现登陆用户认证授权

关于Spring Security的介绍就不再多说,直接上代码整合,此次整合使用的是SQL server数据库,mybatis进行整合

步骤一:

建立spring boot项目,并引入相关的jar包,pom.xml文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <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.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>


        <!--for mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <!--for SqlServer-->
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>sqljdbc4</artifactId>
            <version>4.0</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                </configuration>
            </plugin>


            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.2</version>
                <configuration>
                    <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
                    <verbose>true</verbose>
                    <overwrite>true</overwrite>
                </configuration>

            </plugin>


        </plugins>
    </build>

</project>

步骤二:建立数据库,目前使用了六个表,分别是用户表,登陆表,角色表,页面资源表,用户-角色表,角色-资源页面访问表

sql语句如下所示:

/*
Navicat SQL Server Data Transfer

Source Server         : yys_sqlserver
Source Server Version : 105000
Source Host           : localhost:1433
Source Database       : SpringSecurity_Test
Source Schema         : dbo

Target Server Type    : SQL Server
Target Server Version : 105000
File Encoding         : 65001

Date: 2020-03-21 14:27:17
*/


-- ----------------------------
-- Table structure for login
-- ----------------------------
DROP TABLE [dbo].[login]
GO
CREATE TABLE [dbo].[login] (
[id] varchar(36) NOT NULL ,
[loginUserName] varchar(36) NOT NULL ,
[loginPassword] varchar(255) NOT NULL ,
[userId] varchar(36) NOT NULL 
)


GO

-- ----------------------------
-- Table structure for page
-- ----------------------------
DROP TABLE [dbo].[page]
GO
CREATE TABLE [dbo].[page] (
[id] varchar(36) NOT NULL ,
[pageName] varchar(255) NOT NULL ,
[pageUrl] varchar(255) NOT NULL 
)


GO

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE [dbo].[role]
GO
CREATE TABLE [dbo].[role] (
[id] varchar(36) NOT NULL ,
[roleName] varchar(255) NOT NULL ,
[roleRemark] varchar(36) NULL 
)


GO

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE [dbo].[user]
GO
CREATE TABLE [dbo].[user] (
[id] varchar(36) NOT NULL ,
[username] varchar(36) NOT NULL 
)


GO

-- ----------------------------
-- Table structure for user_Page
-- ----------------------------
DROP TABLE [dbo].[user_Page]
GO
CREATE TABLE [dbo].[user_Page] (
[id] varchar(36) NOT NULL ,
[userRoleID] varchar(36) NOT NULL ,
[pageId] varchar(36) NOT NULL ,
[roleCode] varchar(20) NULL 
)


GO

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE [dbo].[user_role]
GO
CREATE TABLE [dbo].[user_role] (
[id] varchar(36) NOT NULL ,
[userid] varchar(36) NOT NULL ,
[roleId] varchar(36) NOT NULL 
)


GO

-- ----------------------------
-- Indexes structure for table login
-- ----------------------------

-- ----------------------------
-- Primary Key structure for table login
-- ----------------------------
ALTER TABLE [dbo].[login] ADD PRIMARY KEY ([id])
GO

-- ----------------------------
-- Indexes structure for table page
-- ----------------------------

-- ----------------------------
-- Primary Key structure for table page
-- ----------------------------
ALTER TABLE [dbo].[page] ADD PRIMARY KEY ([id])
GO

-- ----------------------------
-- Indexes structure for table role
-- ----------------------------

-- ----------------------------
-- Primary Key structure for table role
-- ----------------------------
ALTER TABLE [dbo].[role] ADD PRIMARY KEY ([id])
GO

-- ----------------------------
-- Indexes structure for table user
-- ----------------------------

-- ----------------------------
-- Primary Key structure for table user
-- ----------------------------
ALTER TABLE [dbo].[user] ADD PRIMARY KEY ([id])
GO

-- ----------------------------
-- Indexes structure for table user_Page
-- ----------------------------

-- ----------------------------
-- Primary Key structure for table user_Page
-- ----------------------------
ALTER TABLE [dbo].[user_Page] ADD PRIMARY KEY ([id])
GO

-- ----------------------------
-- Indexes structure for table user_role
-- ----------------------------

-- ----------------------------
-- Primary Key structure for table user_role
-- ----------------------------
ALTER TABLE [dbo].[user_role] ADD PRIMARY KEY ([id])
GO

数据库中表得数据如下:

 

 

步骤三:根据数据库使用mybatis自动生成bean,service等Java类,目录结构如下所示:

generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC " -//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <classPathEntry
            location="target/mssql-jdbc-7.4.1.jre8.jar"/>
    <context id="my" targetRuntime="MyBatis3">
        <commentGenerator>
            <property name="suppressDate" value="false"/>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <jdbcConnection driverClass="com.microsoft.sqlserver.jdbc.SQLServerDriver"
                        connectionURL="jdbc:sqlserver://127.0.0.1:1433;Databasename=SpringSecurity_Test"
                        userId="sa"
                        password="123456">
        </jdbcConnection>
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
        <javaModelGenerator targetPackage="com.example.demo.bean"
                            targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <sqlMapGenerator targetPackage="mapper"
                         targetProject="src/main/resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>
        <javaClientGenerator targetPackage="com.example.demo.mapper"
                             targetProject="src/main/java" type="XMLMAPPER">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>
        <table tableName="login" domainObjectName="login"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false">
        </table>

        <table tableName="page" domainObjectName="page"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false">
        </table>

        <table tableName="role" domainObjectName="role"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false">
        </table>

        <table tableName="user" domainObjectName="user"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false">
        </table>
        <table tableName="user_Page" domainObjectName="user_Page"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false">
        </table>
        <table tableName="user_role" domainObjectName="user_role"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false">
        </table>
    </context>
</generatorConfiguration>

步骤四:编写security的Config类,进行security配置:

package com.example.demo.config;

import com.example.demo.DetailService.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
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.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * @ClassName securityConfig
 * @Description TODO
 * @Author YYS
 * @Date 2020/3/17 22:08
 * @Version 1.0
 */
@EnableWebSecurity
public class securityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    CustomUserDetailsService userDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //关闭防止跨站攻击
        http.csrf().disable();
        //授权
        //首页所有人可以访问,但是功能页只有有权限的人访问
        //这个配置写的不太完善,仅供参考
        http.authorizeRequests().antMatchers("/","/login","/index")
                .permitAll()
                .antMatchers("/index1/**").hasAuthority("P1")
                .antMatchers("/index2/**").hasAuthority("P2")
                .and()
                //登陆成功之后根据权限不同跳转至不同页面
//                .formLogin().loginPage("/login").successHandler(
//                        new AuthenticationSuccessHandler() {
//                            @Override
//                            public void onAuthenticationSuccess(HttpServletRequest arg0, HttpServletResponse arg1, Authentication arg2)
//                                    throws IOException, ServletException {
//                                Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
//                                if (principal != null && principal instanceof UserDetails) {
//                                    UserDetails user = (UserDetails) principal;
//                                    System.out.println("loginUser:"+user.getUsername());
//                                    //维护在session中
//                                    arg0.getSession().setAttribute("userDetail", user);
//                                    Set<String> roles = AuthorityUtils.authorityListToSet(SecurityContextHolder.getContext().getAuthentication().getAuthorities());
//                                    for (String role: roles){
//                                        System.out.println(role);
//                                        if (role.equals("P1")){
//                                            arg1.sendRedirect("/index1");
//                                        }else {
//                                            arg1.sendRedirect("/index2");
//                                        }
//                                    }
//                                }
//                            }
//                    })
                //登陆成功之后跳转至统一页面
                .formLogin().loginPage("/login").successForwardUrl("/index").failureForwardUrl("/loginerror")
                .and()//注销后会发送请求至Controller,然后根据“/”请求跳转至登陆页
                .logout().logoutUrl("/");
        //开启记住我
        http.rememberMe();
    }

    //认证
    //There is no PasswordEncoder mapped for the id "null"
    //密码要加密,spring secutiry5.0中,新增了许多加密方式
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //基于内存的认证
//        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
//                .withUser("yys1").password(new BCryptPasswordEncoder().encode("123456")).roles("P1")
//                .and()
//                .withUser("yys2").password(new BCryptPasswordEncoder().encode("123456")).roles("P2");
        //基于数据库的认证
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }

}

步骤五:实现UserDetailsService接口,并在其中编写自己的逻辑,从数据库中查找到当前登陆用户的用户名密码,并与输入的进行比对,判断是否可以登陆

CustomUserDetailsService.java
package com.example.demo.DetailService;
import com.example.demo.bean.login;
import com.example.demo.bean.role;
import com.example.demo.bean.user;
import com.example.demo.bean.user_Page;
import com.example.demo.service.LoginService;
import com.example.demo.service.RoleService;
import com.example.demo.service.UserPageService;
import com.example.demo.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

/**
 * @Author YYS
 * @Description //TODO  
 * @Date 23:39 23:39
 * @Param 
 * @return
 **/
@Component
public class CustomUserDetailsService implements UserDetailsService {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomUserDetailsService.class);

    private UserService userService;

    @Autowired
    private RoleService roleService;

    private BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();

    @Autowired
    private UserPageService userPageService;

    @Autowired
    LoginService loginService;

    @Autowired
    public CustomUserDetailsService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        LOGGER.info("对用户 [{}] 进行信息加载...",username);

        user user = userService.findUserByUsername(username);
        /* 从数据库里面查出当前登录用户 */
        login login=loginService.getLoginByUserName(username);
        //登陆用户不存在,直接返回null,系统会直接抛出异常
        if(user==null){
            LOGGER.error("用户 [{}] 未找到",username);
            return null;
        }
        //以下两个是分别获取登陆用户的角色和权限,可以选择利用角色作为授权,或者权限作为授权
        Collection<GrantedAuthority> roleauthorities = new ArrayList<>();
        Collection<GrantedAuthority> pageauthorities = new ArrayList<>();
        //角色
        Set<role> roles = roleService.roles(username);
        //权限
        List<user_Page> user_pages = userPageService.user_Page_List(username);
        //将用户角色放入roleauthorities
        if(roles != null){
            for (role role : roles) {
                SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.getRolename());
                roleauthorities.add(authority);
            }
        }

        //将用户权限放入pageauthorities
        if (user_pages !=null && !user_pages.isEmpty()){
            for (user_Page userPage:user_pages){
                SimpleGrantedAuthority authority = new SimpleGrantedAuthority(userPage.getRoleCode());
                pageauthorities.add(authority);
            }
        }
        //根据角色授权还是根据权限授权自己选,此处,我根据权限授权,判断用户是否有访问页面的权限
        UserDetails roleuserDetails = new User(login.getLoginusername(),bCryptPasswordEncoder.encode(login.getLoginpassword()),roleauthorities);
        UserDetails pageuserDetails = new User(login.getLoginusername(),bCryptPasswordEncoder.encode(login.getLoginpassword()),pageauthorities);
        LOGGER.info("用户 [{}] 信息加载完成",username);
        LOGGER.info("获取登录用户已具有的角色:{}", roleauthorities.toString());
        LOGGER.info("获取登录用户已具有的权限:{}", pageauthorities.toString());
        //返回userDetails
        return pageuserDetails;
    }
}

Controller文件:

package com.example.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @ClassName Controller
 * @Description TODO
 * @Author YYS
 * @Date 2020/3/17 21:38
 * @Version 1.0
 */
@org.springframework.stereotype.Controller
public class Controller {

    @RequestMapping({"/", "/login"})
    public String login() {
        return "login";
    }

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

    @RequestMapping({"/index1"})
    public String index1() {
        return "index1";
    }

    @RequestMapping({"/index2"})
    public String index2() {
        return "index2";
    }

    @RequestMapping("/logout")
    public String logout() {
        return "login";
    }

    @RequestMapping("/loginerror")
    public String loginerror() {
        return "loginerror";
    }

}

application.propies文件:

spring.thymeleaf.cache=false

server.port=8080

# 给返回的页面添加后缀名
spring.mvc.view.suffix=.html

# mybatis 配置
mybatis.type-aliases-package=cn.codesheep.springbt_mybatis_sqlserver.entity
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.configuration.map-underscore-to-camel-case=true

## -------------------------------------------------

## SqlServer 配置
spring.datasource.url=jdbc:sqlserver://127.0.0.1:1433;databasename=SpringSecurity_Test
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.username=sa
spring.datasource.password=123456

html文件结构:

步骤六:启动项目

访问localhost:8080

使用yys正确登录:

后台结果显示:

此时,用户只有访问index1的权限,无访问index2的权限

访问index1:

访问index2:

在使用可以访问index2的用户登录:

访问有权限的index2

代码地址:https://github.com/yys-518/SpringBoot-SpringSecuity

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值