springboot3.0.2,security6.0.1,my

2023-02-17  本文已影响0人  不知不怪

点我下载完整源码

1 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 http://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>3.0.2</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.gzz</groupId>
    <artifactId>spring-security-06</artifactId>
    <version>1.0</version>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2 简洁的资源鉴权

package com.gzz.config;

import java.util.List;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;

/**
 * @summary 鉴权-资源验证处理
 * @author 高振中
 * @date 2023-02-18 22:20:20
 **/
@Slf4j
@Component("resourcesCheck")
public class ResourcesCheck {
    public boolean check(Authentication authentication, HttpServletRequest request) {
        // 如果要解析token就从request中取出就可以了
        // 再如果不使用session,参数authentication也没有用了,只需从token反解出userId然后从缓存中取出authentication
        // 这时可以在Security主配置文件中关闭session
        // 这种方式不用任何注解,就可以对资源(controller中requestMapping的value)也就是请求路径进行最灵活的配置(在数据库中)
        Object principal = authentication.getPrincipal();
        if (principal instanceof UserDetails) {
            SysUser sysUser = (SysUser) principal;
            List<String> resources = sysUser.getResources();
            log.info("sysUser={},resources={},request.getRequestURI()={}", sysUser, resources, request.getRequestURI());
            return resources.contains(request.getRequestURI());
        }
        return false;
    }
}
2023-02-18 21:42:33,027[INFO][FrameworkServlet.java:554]:Completed initialization in 1 ms
2023-02-18 21:42:33,111[INFO][HikariDataSource.java:110]:test - Starting...
2023-02-18 21:42:33,323[INFO][HikariPool.java:565]:test - Added connection com.mysql.cj.jdbc.ConnectionImpl@774f3160
2023-02-18 21:42:33,325[INFO][HikariDataSource.java:123]:test - Start completed.
2023-02-18 21:42:35,833[INFO][ResourcesCheck.java:29]:sysUser=com.gzz.config.SysUser [Username=高振中, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]],resources=[/user/add, /user/update],request.getRequestURI()=/error
2023-02-18 21:42:35,852[INFO][ResourcesCheck.java:29]:sysUser=com.gzz.config.SysUser [Username=高振中, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]],resources=[/user/add, /user/update],request.getRequestURI()=/error
2023-02-18 21:42:35,866[INFO][ResourcesCheck.java:29]:sysUser=com.gzz.config.SysUser [Username=高振中, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]],resources=[/user/add, /user/update],request.getRequestURI()=/favicon.ico
2023-02-18 21:42:38,534[INFO][ResourcesCheck.java:29]:sysUser=com.gzz.config.SysUser [Username=高振中, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]],resources=[/user/add, /user/update],request.getRequestURI()=/error
2023-02-18 21:42:38,560[INFO][ResourcesCheck.java:29]:sysUser=com.gzz.config.SysUser [Username=高振中, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]],resources=[/user/add, /user/update],request.getRequestURI()=/error
2023-02-18 21:42:38,576[INFO][ResourcesCheck.java:29]:sysUser=com.gzz.config.SysUser [Username=高振中, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]],resources=[/user/add, /user/update],request.getRequestURI()=/favicon.ico
2023-02-18 21:43:02,813[INFO][ResourcesCheck.java:29]:sysUser=com.gzz.config.SysUser [Username=高振中, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]],resources=[/user/add, /user/update],request.getRequestURI()=/user/add
2023-02-18 21:43:09,627[INFO][ResourcesCheck.java:29]:sysUser=com.gzz.config.SysUser [Username=高振中, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]],resources=[/user/add, /user/update],request.getRequestURI()=/user/list
2023-02-18 21:43:22,458[INFO][LogoutSuccess.java:23]:authentication=UsernamePasswordAuthenticationToken [Principal=com.gzz.config.SysUser [Username=高振中, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=1E935E5AD193CDB5C1F64515034A0216], Granted Authorities=[ROLE_USER]]

3 建表

CREATE TABLE `user`  (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
  `code` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '编码',
  `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '姓名',
  `password` varchar(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '密码',
  `roles` varchar(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '角色集',
  `resources` varchar(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '资源集',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

INSERT INTO `user` VALUES (1, 'zhang', '张三', '$2a$10$JejfDoeJ7dn/xUPI051JDO1Jr1H/9MnlXqa7GChitOqlcSfjwyxxW', 'ROLE_ADMIN,ROLE_USER', '/user/add,/user/update,/user/list,/user/delete');
INSERT INTO `user` VALUES (2, 'lisi', '李四', '$2a$10$JejfDoeJ7dn/xUPI051JDO1Jr1H/9MnlXqa7GChitOqlcSfjwyxxW', 'ROLE_ADMIN,ROLE_USER', '/user/add,/user/update,/user/list,/user/delete');
INSERT INTO `user` VALUES (3, 'gzz', '高振中', '$2a$10$JejfDoeJ7dn/xUPI051JDO1Jr1H/9MnlXqa7GChitOqlcSfjwyxxW', 'ROLE_USER', '/user/add,/user/update');
INSERT INTO `user` VALUES (4, 'fbb', '范冰冰', '$2a$10$JejfDoeJ7dn/xUPI051JDO1Jr1H/9MnlXqa7GChitOqlcSfjwyxxW', 'ROLE_ADMIN', '/user/list,/user/delete');
1676728473621.png

4 spring-security主配置类

package com.gzz.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.expression.DefaultHttpSecurityExpressionHandler;
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;

import com.gzz.config.handler.AccessDenied;
import com.gzz.config.handler.LoginFailure;
import com.gzz.config.handler.LoginSuccess;
import com.gzz.config.handler.LogoutSuccess;
import com.gzz.sys.UserService;

/**
 * @summary spring-security主配置类
 * @author 高振中
 * @date 2023-02-18 22:20:20
 **/
@Configuration
public class SecurityConfig {
    @Autowired
    private LoginSuccess loginSuccess;
    @Autowired
    private LoginFailure loginFailure;
    @Autowired
    private AccessDenied accessDenied;
    @Autowired
    private EntryPoint entryPoint;
    @Autowired
    private LogoutSuccess logoutSuccess;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity security) throws Exception {
        logoutSuccess.setDefaultTargetUrl("/logout_success");
        security//
                .authorizeHttpRequests(auth -> auth //
                        .requestMatchers("/index.html", "/fonts/**", "/logout_success").permitAll() // 这些请求(url)无需授权
                        .anyRequest().access(expressionManager()))
                .formLogin((auth) -> auth//
                        .usernameParameter("username") //
                        .passwordParameter("password") //
                        .loginPage("/index.html") //
                        .loginProcessingUrl("/login") //
                        .successHandler(loginSuccess) //
                        .failureHandler(loginFailure) //
                        .permitAll())
                .logout(auth -> auth//
                        .deleteCookies("JSESSIONID")//
                        .logoutSuccessHandler(logoutSuccess).clearAuthentication(true))
                .exceptionHandling(exp -> exp//
                        .accessDeniedPage("/index.html")//
                        .accessDeniedHandler(accessDenied)//
                        .authenticationEntryPoint(entryPoint))//
                .csrf(csrf -> csrf.disable());
        return security.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    private UserService service;

    public void configureGlobal(@Autowired AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(service).passwordEncoder(passwordEncoder());
    }

    @Autowired
    private ApplicationContext applicationContext;

    @Bean
    public WebExpressionAuthorizationManager expressionManager() {
        DefaultHttpSecurityExpressionHandler expressionHandler = new DefaultHttpSecurityExpressionHandler();
        expressionHandler.setApplicationContext(applicationContext);
        WebExpressionAuthorizationManager authorizationManager = new WebExpressionAuthorizationManager("@resourcesCheck.check(authentication,request)");
        authorizationManager.setExpressionHandler(expressionHandler);
        return authorizationManager;
    }
}

5 UserService

package com.gzz.sys;

import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
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.Service;
import org.springframework.util.CollectionUtils;
import com.gzz.config.SysUser;

/**
 * @类说明 spring-security从数据库加载用户相关的资源数据
 * @author 高振中
 * @date 2023-02-18 22:23:28
 **/
@Service
public class UserService implements UserDetailsService {

    @Autowired
    private UserDao userDao; // 注入【user】数据访问层
    // 1.不论数据库如何设计,最终都是要解决【用户】拥有哪些【角色】最终分配了多少【资源】的问题,RBAC不在security的讨论范围
    // 2.假定数据已经组装好,最终从数据库里取到的是【用户名,密码,角色集,资源集】这些数据,然后看在security中如何使用

    @Override
    public UserDetails loadUserByUsername(String code) {

        List<User> users = userDao.list(code);
        if (CollectionUtils.isEmpty(users))
            throw new UsernameNotFoundException("用户不存在");
        User user = users.get(0);
        List<String> roles = Arrays.asList(user.getRoles().split(","));
        List<String> resources = Arrays.asList(user.getResources().split(","));
        return new SysUser(user.getName(), user.getPassword(), roles, resources);

    }
}

6 点我下载完整源码

上一篇下一篇

猜你喜欢

热点阅读