springboot+springSecurity+thymeleaf整合

在这里咱们做个springboot+springSecurity+thymeleaf的整合,才用的框架是springboot+mybatisPlus,所有的用户数据,角色数据,权限数据均来自于数据库。

1 项目结构和数据库结构

为了便于了解整个项目结构,先看下项目结构和数据库结构

1.1 项目结构

在这里插入图片描述

1.2 数据库结构

在这里插入图片描述
建表和数据语句如下:

CREATE DATABASE /*!32312 IF NOT EXISTS*/`user_db` /*!40100 DEFAULT CHARACTER SET utf8 */ /*!80016 DEFAULT ENCRYPTION='N' */;

USE `user_db`;

/*Table structure for table `t_permission` */

DROP TABLE IF EXISTS `t_permission`;

CREATE TABLE `t_permission` (
  `id` varchar(32) NOT NULL,
  `code` varchar(32) NOT NULL COMMENT '权限标识符',
  `description` varchar(64) DEFAULT NULL COMMENT '描述',
  `url` varchar(128) DEFAULT NULL COMMENT '请求地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Data for the table `t_permission` */

insert  into `t_permission`(`id`,`code`,`description`,`url`) values ('1','p1','测试资源 1','/r/r1'),('2','p2','测试资源2','/r/r2');

/*Table structure for table `t_role` */

DROP TABLE IF EXISTS `t_role`;

CREATE TABLE `t_role` (
  `id` varchar(32) NOT NULL,
  `role_name` varchar(255) DEFAULT NULL,
  `description` varchar(255) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `status` char(1) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `unique_role_name` (`role_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Data for the table `t_role` */

insert  into `t_role`(`id`,`role_name`,`description`,`create_time`,`update_time`,`status`) values ('1','管理员',NULL,NULL,NULL,'');

/*Table structure for table `t_role_permission` */

DROP TABLE IF EXISTS `t_role_permission`;

CREATE TABLE `t_role_permission` (
  `role_id` varchar(32) NOT NULL,
  `permission_id` varchar(32) NOT NULL,
  PRIMARY KEY (`role_id`,`permission_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Data for the table `t_role_permission` */

insert  into `t_role_permission`(`role_id`,`permission_id`) values ('1','1'),('1','2');

/*Table structure for table `t_user` */

DROP TABLE IF EXISTS `t_user`;

CREATE TABLE `t_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户id',
  `username` varchar(64) NOT NULL,
  `password` varchar(64) NOT NULL,
  `fullname` varchar(255) NOT NULL COMMENT '用户姓名',
  `mobile` varchar(11) DEFAULT NULL COMMENT '手机号',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

/*Data for the table `t_user` */

insert  into `t_user`(`id`,`username`,`password`,`fullname`,`mobile`) values (1,'lisi','$2a$10$aFsOFzujtPCnUCUKcozsHux0rQ/3faAHGFSVb9Y.B1ntpmEhjRtru','李四','123');

/*Table structure for table `t_user_role` */

DROP TABLE IF EXISTS `t_user_role`;

CREATE TABLE `t_user_role` (
  `user_id` varchar(32) NOT NULL,
  `role_id` varchar(32) NOT NULL,
  `create_time` datetime DEFAULT NULL,
  `creator` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`user_id`,`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Data for the table `t_user_role` */

insert  into `t_user_role`(`user_id`,`role_id`,`create_time`,`creator`) values ('1','1',NULL,NULL);

2 引入依赖

<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.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</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>
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.3.2</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
	</dependencies>

3 配置application.yml

server:
  port: 8080
spring:
  thymeleaf:
    mode: LEGACYHTML5
  main:
    allow-bean-definition-overriding: true
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/user_db?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      minimum-idle: 5
      ## 空闲连接存活最大时间,默认600000(10分钟)
      idle-timeout: 600000
      ## 连接池最大连接数,默认是10
      maximum-pool-size: 100
      ## 此属性控制从池返回的连接的默认自动提交行为,默认值:true
      auto-commit: true
      ## 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
      max-lifetime: 1800000
      ## 数据库连接超时时间,默认30秒,即30000
      connection-timeout: 30000
      connection-test-query: SELECT 1
user:
  key: SESSION_USER_KEY

4 login.html

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head><title>用户登录</title></head>
<body>
<form action="/signLogin" method="post">
    姓名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br> <input type="submit" value="登录">
</form>
</body>
</html>

5 配置类(config包)

5.1 WebAppConfigurer

该类主要是用于访问路径的重定向,分为两步:
第一步:首先是根目录访问的时候,直接重定向到登录界面。
第二步:然后直接定位到自定义的登录界面。

package com.wuk.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {


    /**
     * 默认Url根路径跳转到/login,此url为spring security提供
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {

        //url根路径跳转到/login-view
        registry.addViewController("/").setViewName("redirect:/login-view");
        // /login-view定位到自定义登录页面
        registry.addViewController("/login-view").setViewName("/user/login");
    }
}

5.2 WebSecurityConfig

该类是安全配置类,安全配置的内容包括:用户信息、密码编码器、安全拦截机制。

package com.wuk.config;

import org.springframework.context.annotation.Bean;
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;

/**
 * @Description:
 * @Author: wuk
 * @CreateDate: 2020/8/18 14:17
 * @UpdateDate: 2020/8/18 14:17
 * @UpdateRemark: init
 * @Version: 1.0
 * @menu
 */
@EnableGlobalMethodSecurity(prePostEnabled = true) //开启注解校验权限
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 配置安全拦截机制
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
                .authorizeRequests()
//                .antMatchers("/r/*").authenticated()
                //方式1:在这里配置,方式2:在具体接口上配置
//                .antMatchers("/r/r1").hasAuthority("p1")
//                .antMatchers("/r/r2").hasAuthority("p2")
                .anyRequest().permitAll() //其他的统一放行
                .and()
                .formLogin()
                .loginPage("/login-view")
                .loginProcessingUrl("/signLogin")
                .defaultSuccessUrl("/login/success") //这里只能是defaultSuccessUrl而不能是successForwardUrl 否则报405
                .permitAll()
                .and()
                .csrf().disable();
    }


    /**
     * 配置用户信息服务 第一次写死,后续自动加载自定义的userDetailsService
     */
//    @Override
//    @Bean
//    public UserDetailsService userDetailsService() {
//        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
//        manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
//        manager.createUser(User.withUsername("lisi").password("123").authorities("p2").build());
//        return manager;
//    }

    /**
     * 密码加密方式
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


}

5.3 MyUserDetailsService
package com.wuk.config;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wuk.entity.Permission;
import com.wuk.entity.RolePermission;
import com.wuk.entity.User;
import com.wuk.entity.UserRole;
import com.wuk.mapper.PermissionMapper;
import com.wuk.mapper.RolePermissionMapper;
import com.wuk.mapper.UserMapper;
import com.wuk.mapper.UserRoleMapper;
import lombok.extern.slf4j.Slf4j;
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.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Description:
 * @Author: wuk
 * @CreateDate: 2020/8/22 9:59
 * @UpdateDate: 2020/8/22 9:59
 * @UpdateRemark: init
 * @Version: 1.0
 * @menu
 */
@Slf4j
@Component
public class MyUserDetailsService implements UserDetailsService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private UserRoleMapper userRoleMapper;

    @Resource
    private RolePermissionMapper rolePermissionMapper;

    @Resource
    private PermissionMapper permissionMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        List<User> users = userMapper.selectByMap(new HashMap<String, Object>() {{
            put("username", username);
        }});
        if (CollectionUtils.isEmpty(users)) {
            log.info("账号输入错误,查询不到用户");
            return null;
        }

        User user = users.get(0);
        List<UserRole> userRoles = userRoleMapper.selectByMap(new HashMap<String, Object>() {{
            put("user_id", user.getId());
        }});
        log.info("userRoles ={}",userRoles);
        if (CollectionUtils.isEmpty(userRoles)) {
            log.info("该用户尚未分配角色");
            return null;
        }
        List<Long> roleIds = userRoles.stream().map(UserRole::getRoleId).distinct().collect(Collectors.toList());
        log.info("roleIds ={}",roleIds);
        List<RolePermission> rolePermissions = rolePermissionMapper.selectList(new QueryWrapper<RolePermission>().in("role_id", roleIds));
        log.info("rolePermissions ={}",rolePermissions);
        if(CollectionUtils.isEmpty(rolePermissions)){
            log.info("所有均角色尚未绑定权限");
            return null;
        }
        List<Long> permissionIds = rolePermissions.stream().map(RolePermission::getPermissionId).distinct().collect(Collectors.toList());
        log.info("permissionIds ={}",permissionIds);
        List<Permission> permissions = permissionMapper.selectBatchIds(permissionIds);
        log.info("permissions ={}",permissions);
        if(CollectionUtils.isEmpty(permissions)){
            log.info("权限不存在");
            return null;
        }
        List<String> codes = permissions.stream().map(Permission::getCode).distinct().collect(Collectors.toList());
        String[] perArray = new String[codes.size()];
        codes.toArray(perArray);

        return org.springframework.security.core.userdetails.User
                .withUsername(user.getFullname())
                .password(user.getPassword())
                .authorities(perArray).build();
    }
}

5.4 实体类(entity)
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("t_permission")
public class Permission {

    @TableId(value = "id",type = IdType.AUTO)
    private Long id;

    private String code;

    private String description;

    private String url;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_role")
public class Role {

    @TableId(value = "id",type = IdType.AUTO)
    private Long id;

    private String roleName;

    private String description;

    private Date updateTime;

    private Integer status;

}
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_role_permission")
public class RolePermission {

    private Long roleId;

    private Long permissionId;

}
@Data
@TableName("t_user")
public class User {

    @TableId(value = "id",type = IdType.AUTO)
    private Long id;

    private String username;

    private String password;

    private String fullname;

    private String mobile;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_user_role")
public class UserRole {

    private Long userId;

    private Long roleId;

    private Date createTime;

    private String creator;
}
5.5 mapper
public interface PermissionMapper extends BaseMapper<Permission> {

}
public interface RoleMapper extends BaseMapper<Role> {

}
public interface RolePermissionMapper extends BaseMapper<RolePermission> {

}
public interface UserMapper extends BaseMapper<User> {

}
public interface UserRoleMapper extends BaseMapper<UserRole> {

}
@SpringBootApplication
@MapperScan("com.wuk.mapper")
public class OauthtestApplication {

	public static void main(String[] args) {
		SpringApplication.run(OauthtestApplication.class, args);
	}

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术闲聊DD

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值