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
//如果不设置自己的登录和注销会跳到默认的登录页面和注销功能
//默认的方法
SpringBoot整合SpringSecurity_第1张图片
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



<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";
    }

}

测试页面的代码
SpringBoot整合SpringSecurity_第2张图片
common.html(公共元素页)

    
    
    <head>
        <meta charset="UTF-8">
        <title>Titletitle>
    head>
    <body>
    
    <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


<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>level1title>
head>
<body>

<div th:insert="~{common::toIndex}">div>
<div th:insert="~{common::Info}">div>

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

index.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>

<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}">访问vip1a>div><br>
<hr>
<div sec:authorize="hasRole('ROLE_ADMIN')">
<a th:href="@{/level2}">访问vip2a>div><br>
<hr>
    <div sec:authorize="hasRole('ROLE_ADMIN')">
<a th:href="@{/level3}">访问vip3a>div><br>
<hr>
body>
html>

login.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>

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

在这里插入图片描述
账号密码错误
SpringBoot整合SpringSecurity_第3张图片
当登录后获得权限:显示有权限的模块
SpringBoot整合SpringSecurity_第4张图片

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

你可能感兴趣的:(笔记)