SpringBoot整合SpringSecurity

1.导入核心依赖

<!--thymeleaf-springsecurity整合-->
 <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
            <version>3.0.4.RELEASE</version>
</dependency>
<!--SpringSecurity起步依赖-->
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
</dependency>

//使用数据库账号密码登录(数据库动态管理用户、角色、权限)基于mybatis
//如果不设置自己的登录和注销会跳到默认的登录页面和注销功能
//默认的方法
在这里插入图片描述
sql表

DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `perms` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `role_user`;
CREATE TABLE `role_user` (
  `id` int(20) NOT NULL AUTO_INCREMENT,
  `user_id` int(20) NOT NULL,
  `role_id` int(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

application.yml配置

spring:
  thymeleaf:
    cache: false

  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: 
    username: 
    password: 
    #指定数据源
    type: com.alibaba.druid.pool.DruidDataSource
    #druid数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedstatements: true
    #配置监控统计拦截的filters, stat: 监控统计、log4j: 日志记录、wall: 防御Isql注入
    #如果允许时报错java. Lang. ClassNotFoundException: org. apache. Log4j. Priority
    #则导入,Log4j 依赖即可,Maven地址: https://mvnrepository. com/artifact/Log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
  main:
    allow-bean-definition-overriding: true
mybatis:
  mapper-locations: classpath:mybatis/mapper/*.xml
  type-aliases-package: com.lwz.entity

实体类
//用户表

package com.lwz.entity;

import java.util.List;

public class User {
    private int id;
    private String username;
    private String password;
    private  String perms;
    private List<Role> roles;
    public User() {
    }

    public User(int id, String username, String password, String perms, List<Role> roles) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.perms = perms;
        this.roles = roles;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPerms() {
        return perms;
    }

    public void setPerms(String perms) {
        this.perms = perms;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", perms='" + perms + '\'' +
                ", roles=" + roles +
                '}';
    }
}

//角色表

package com.lwz.entity;

import java.util.List;

public class Role {
    private int id;
    private String name;
    private List<User> users;

    public Role() {
    }

    public Role(int id, String name, List<User> users) {
        this.id = id;
        this.name = name;
        this.users = users;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", users=" + users +
                '}';
    }
}

Mapper接口

@Repository
public interface UserMapper {
    @Autowired

    public User findUserByUname(String name);

}

XML

<?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="com.lwz.mapper.UserMapper">
    <resultMap id="user_role" type="com.lwz.entity.User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <collection property="roles" ofType="com.lwz.entity.Role" >
            <id property="id" column="rid" />
            <result property="name" column="name"/>
        </collection>
    </resultMap>
	<!--注意此处的字段名和属性名一定要对上,否则无法正常封装-->
    <select id="findUserByUname" resultMap="user_role" parameterType="string">
        SELECT u.*,r.id as rid,r.`name`  from  `user` u
        LEFT JOIN role_user ru ON u.id=ru.user_id
        LEFT JOIN role r ON ru.role_id=r.id
        where  u.username=#{name}
    </select>

</mapper>

此处省略Service层

自定义UserDetailsService 接口
实现动态认证的核心接口

package com.lwz.service.impl;

import com.lwz.entity.Role;
import com.lwz.entity.User;
import com.lwz.service.UserService;

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.Component;
import java.util.ArrayList;
import java.util.List;
//自定义UserDetailsService 接口
@Component
public class CustomUserService implements UserDetailsService {
    @Autowired
    UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.findUserByUname(username);
        if (user==null){
            throw new UsernameNotFoundException("用户名不存在");
        }
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        //用于添加用户的权限。只要把用户权限添加到authorities 就万事大吉。
        //一个用户对应多个角色
        for (Role role:user.getRoles()){
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        
        return new org.springframework.security.core.userdetails.User(user.getUsername(),new BCryptPasswordEncoder().encode(user.getPassword()),authorities);
    }
}

自定义登录成功处理

package com.lwz.config;

import com.lwz.entity.User;
import com.lwz.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

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

/**
 * 自定义登录成功处理
 */
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    public static final String RETURN_TYPE = "html";

    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Autowired
    UserMapper userMapper;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        //获取用户名
        String username = request.getParameter("username");
        //通过用户名查找用户信息
        User user= userMapper.findUserByUname(username);
        System.out.println(user);
        if (RETURN_TYPE.equals("html")){
            //把用户信息存进session里方便前端显示
            request.getSession().setAttribute("user",user);
            request.getRequestDispatcher("index").forward(request,response);
//            redirectStrategy.sendRedirect(request,response,"index");
        }
    }
}

自定义登录失败处理

package com.lwz.config;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

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

/**
 * 自定义登录失败处理
 */
@Component//添加为组件,方便config获取
public class MyAuthenticationFailHandler implements AuthenticationFailureHandler {

    public static final String RETURN_TYPE = "html"; // 登录失败时,用来判断是返回json数据还是跳转html页面

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        if (RETURN_TYPE.equals("html")){
            //返回msg提示错误
            request.setAttribute("msg","账号或密码错误");
            System.out.println("自定义登录失败执行了");
            request.getRequestDispatcher("toLogin").forward(request,response);
        }
    }
}

SecurityConfig

package com.lwz.config;

import com.lwz.service.impl.CustomUserService;
import com.lwz.utils.MD5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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;


@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired//注入自定义登录成功处理
    MyAuthenticationSuccessHandler successHandler;
    @Autowired//注入自定义登录失败处理
    MyAuthenticationFailHandler failHandler;

    @Bean
    CustomUserService customUserService(){//注册UserDetailsService 的bean
        return new CustomUserService();
    }


    //链式编程
    //授权
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //首页所有人可以访问,功能也只有对应权限的人才能访问
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasAuthority("ROLE_USER")
                .antMatchers("/level2/**").hasAuthority("ROLE_ADMIN")
                .antMatchers("/level3/**").hasAuthority("ROLE_ADMIN");
        //没有权限默认会到登录页面
        http.formLogin().loginPage("/toLogin").loginProcessingUrl("/login").successHandler(successHandler).failureHandler(failHandler);
        //注销 注销成功跳回首页
        http.csrf().disable();//注销异常时加上这句
        http.logout().logoutSuccessUrl("/");
        //记住我(功能)
        http.rememberMe().rememberMeParameter("remember");


    }


    //认证
    //密码编码:PasswordEncoder
    //在Spring Security 5.0+新增了很多的加密方法~
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        /*auth.userDetailsService(customUserService()).passwordEncoder(new PasswordEncoder(){
            @Override
            public String encode(CharSequence rawPassword) {
                return MD5Util.encrypt((String)rawPassword);
            }
            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                return encodedPassword.equals(MD5Util.encrypt((String)rawPassword));
            }});//user Details Service验证 使用md5加密,这个方法登录失败会报异常,暂时无法解决
            auth.userDetailsService(customUserService());*/
            //从数据库中进行账号密码验证
        auth.userDetailsService(customUserService()).passwordEncoder(new BCryptPasswordEncoder());
        //从内存中进行账号密码验证
//        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
//                .withUser("laiwenzhuo").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2")
//                .and()
//                .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
//                .and()
//                .withUser("zhangsan").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");



    }
}

Controller(路由Controller)

package com.lwz.controller;

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

@Controller
public class RouterController {

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

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

    @RequestMapping("/level1")
    public String level1(){
        return "views/level1/1";
    }
    @RequestMapping("/level2")
    public String level2(){
        return "views/level2/1";
    }
    @RequestMapping("/level3")
    public String level3(){
        return "views/level3/1";
    }

}

测试页面的代码
测试页面的代码
common.html(公共元素页)

    <!DOCTYPE html>
    <html lang="en"xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <!--th:fragment是用于抽取公共元素-->
    <a th:href="@{index}" th:fragment="toIndex">首页</a>

    <div th:fragment="Info">
        <!--如果未登录,显示登录按钮-->
        <div sec:authorize="!isAuthenticated()">
            <a th:href="@{/toLogin}">登录</a><br>
        </div>
        <div sec:authorize="isAuthenticated()">
            <a th:href="@{/logout}">注销</a><br>
        </div>
        <!--如果登录显示用户名和角色-->
    <div sec:authorize="isAuthenticated()">
        用户名:<span sec:authentication="name"></span><br>
        角色:<span sec:authentication="principal.authorities"></span><br>
    </div>
    </div>
    </body>
    </html>

level1

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>level1</title>
</head>
<body>
<!--th:insert:插入公共元素-->
<div th:insert="~{common::toIndex}"></div>
<div th:insert="~{common::Info}"></div>

<hr>
<p>这是vip1访问的页面</p>
</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<p>这是首页</p>
<!--注意此处要先判断是否有session.user这个对象,有则显示,无则不显示,否则会报空异常-->
<div th:if="${!#strings.isEmpty(session.user)}">
id:<p th:text="${session.user.id}"></p>
username:<p th:text="${session.user.username}"></p>
password:<p th:text="${session.user.password}"></p>
roles:<p th:each="role:${session.user.roles}" th:text="${role}"></p><br>
</div>
<!--如果为登录,显示登录按钮-->
<div th:insert="~{common::Info}"></div>
<hr>
<div sec:authorize="hasRole('ROLE_USER')">
<a th:href="@{/level1}">访问vip1</a></div><br>
<hr>
<div sec:authorize="hasRole('ROLE_ADMIN')">
<a th:href="@{/level2}">访问vip2</a></div><br>
<hr>
    <div sec:authorize="hasRole('ROLE_ADMIN')">
<a th:href="@{/level3}">访问vip3</a></div><br>
<hr>
</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登录页</title>
</head>
<body>
<form th:action="@{/login}" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
    <input type="checkbox" name="remember">记住我
    <input type="submit" value="登录"> <br>
</form>
</body>
</html>

效果:
当未登录时不显示指定元素

在这里插入图片描述
账号密码错误
在这里插入图片描述
当登录后获得权限:显示有权限的模块
在这里插入图片描述

参考:
https://blog.csdn.net/shenjianxz/article/details/82348986
https://blog.csdn.net/lizc_lizc/article/details/84059004

Spring Boot整合Spring Security主要是为了提供安全控制功能,帮助开发者快速地在Spring Boot应用添加身份验证、授权和会话管理等安全性措施。以下是基本步骤: 1. 添加依赖:首先,在Maven或Gradle项目添加Spring Security的相关依赖到pom.xml或build.gradle文件。 ```xml <!-- Maven --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- Gradle --> implementation 'org.springframework.boot:spring-boot-starter-security' ``` 2. 配置WebSecurityConfigurerAdapter:在`src/main/resources/application.properties`或application.yml配置一些基础属性,如启用HTTPS、密码加密策略等。然后创建一个实现了`WebSecurityConfigurerAdapter`的类,进行具体的配置,如设置登录页面、认证器、过滤器等。 ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/css/**", "/js/**", "/images/**").permitAll() // 允许静态资源访问 .anyRequest().authenticated() // 所有其他请求需要认证 .and() .formLogin() // 设置基于表单的身份验证 .loginPage("/login") // 登录页URL .defaultSuccessUrl("/") // 登录成功后的默认跳转URL .usernameParameter("username") .passwordParameter("password") .and() .logout() // 注销功能 .logoutUrl("/logout") .logoutSuccessUrl("/") .deleteCookies("JSESSIONID"); } // ... 其他配置如自定义用户DetailsService、密码编码器等 } ``` 3. 用户服务(UserDetailsService):如果需要从数据库或其他数据源获取用户信息,需要实现`UserDetailsService`接口并提供用户查询逻辑。 4. 运行应用:启动Spring Boot应用后,Spring Security将自动处理HTTP请求的安全检查,例如身份验证和授权。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值