springboot 3.1.2 + springsecurity 6.1.2 基于数据库登录演示
引言好久没有创建项目了，spring更新的也太快了，不做不知道，有一些常用的方法都被弃用了，这里就做个简单的演示把。主要还是springSecurity配置文件这块比较重要。文档：官方文档中文文档创建项目pom文件<?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>3.1.2</version> <relativePath/> </parent> <groupId>top.zunmx</groupId> <artifactId>CSM</artifactId> <version>0.0.1-SNAPSHOT</version> <name>CSM</name> <description>CSM</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</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-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.9</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </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> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>springSecurity配置文件package top.zunmx.csm.configure; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; 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.configurers.AbstractHttpConfigurer; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import static org.springframework.security.config.Customizer.withDefaults; @Configuration @EnableWebSecurity public class SecurityConfiguration { @Autowired private UserDetailsService userDetailsService; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); authProvider.setUserDetailsService(userDetailsService); authProvider.setPasswordEncoder(passwordEncoder()); return authProvider; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests((authorizeHttpRequests) -> authorizeHttpRequests .requestMatchers("/login").permitAll() .requestMatchers("/logout").permitAll() .requestMatchers("/login.html").permitAll() // .requestMatchers("/admin").hasRole("ADMIN") .anyRequest().authenticated() ) .formLogin(form -> form .loginProcessingUrl("/login") .loginPage("/login.html") .defaultSuccessUrl("/index.html") .permitAll()) .logout(withDefaults()); http.cors(AbstractHttpConfigurer::disable); http.csrf(AbstractHttpConfigurer::disable); return http.build(); } }userMapperpackage top.zunmx.csm.mapper; import org.apache.ibatis.annotations.Mapper; import top.zunmx.csm.pojo.Users; @Mapper public interface UserMapper { Users getUserByName(String username); }user的pojopackage top.zunmx.csm.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.time.LocalDate; @Data @AllArgsConstructor @NoArgsConstructor public class Users { private Integer id; private String username; private String authcode; private Short status; private String group; private LocalDate createdTime; private LocalDate updateTime; private LocalDate lastLoginTime; private String lastLoginIP; }userDetailsServicepackage top.zunmx.csm.services; 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.stereotype.Component; import top.zunmx.csm.mapper.UserMapper; import top.zunmx.csm.pojo.Users; import java.util.ArrayList; import java.util.List; @Component public class userDetailsService implements UserDetailsService { @Autowired private UserMapper userMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { try { Users user = userMapper.getUserByName(username); GrantedAuthority role; List<GrantedAuthority> authorities = new ArrayList<>(); role = new SimpleGrantedAuthority(user.getGroup()); authorities.add(role); return new User(username, user.getAuthcode(), authorities); } catch (Exception e) { return null; } } }主入口不要忘记添加注解@MapperScan(basePackages = { "top.zunmx.csm.mapper" })UserMapper.xml<?xml version="1.0" encoding="UTF-8" ?> <mapper namespace="top.zunmx.csm.mapper.UserMapper"> SELECT * FROM users where username = #{username} </mapper>登录样例 BeesNet 登录 登录页 登录 spring配置文件server: port: 8798 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/csm username: root password: ****** type: com.alibaba.druid.pool.DruidDataSource druid: initial-size: 20 # 初始连接数 min-idle: 10 # 最小空闲连接数 max-active: 100 # 最大连接数 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: top.zunmx.csm.pojo configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl map-underscore-to-camel-case: false logging: level: root: trace截图
引言我本身对于网络安全是比较感兴趣的，对于网络安全，当前时代还是蛮重要的，想到之前的毕业设计都是围绕着网络安全展开的，废话不多说，最近项目中用到了安全框架，所以进行总结一番。依赖当然，使用maven进行依赖管理还是非常方便的。这里附上我的pom文件<?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> <groupId>com.alc</groupId> <artifactId>security</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <webVersion>3.0</webVersion> <spring.version>4.2.5.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>4.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>4.1.0.RELEASE</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> 1.8 <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <configuration> <port>9090</port> <path>/</path> </configuration> </plugin> </plugins> </build> </project>配置Web.xml<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-security.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>配置spring-security.xml<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <http pattern="/favicon.ico" security="none"/> <http pattern="/login.html" security="none"/> <http pattern="/registry.html" security="none"/> <http pattern="/admin/login.do" security="none"/> <http pattern="/css/*" security="none"/> <http pattern="/js/*" security="none"/> <http pattern="/imgs/*" security="none"/> <http use-expressions="false"> <intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN"/> <form-login login-page="/login.html" default-target-url="http://www.zunmx.top" always-use-default-target="true" authentication-failure-forward-url="/err.html" /> <csrf disabled="true"/> </http> <authentication-manager> <authentication-provider> <user-service> <user name="admin" password="123456" authorities="ROLE_USER"/> </user-service> </authentication-provider> </authentication-manager> </beans:beans>升级版的认证管理器我们肯定不希望走的固定值，最起码走的是数据库，所以配置成走数据库的。<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <beans:bean id="myUserAuth" class="com.alc.auth"/> <http pattern="/favicon.ico" security="none"/> <http pattern="/login.html" security="none"/> <http pattern="/registry.html" security="none"/> <http pattern="/admin/login.do" security="none"/> <http pattern="/css/*" security="none"/> <http pattern="/js/*" security="none"/> <http pattern="/imgs/*" security="none"/> <http use-expressions="false"> <intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN"/> <form-login login-page="/login.html" default-target-url="http://www.zunmx.top" always-use-default-target="true" authentication-failure-forward-url="/err.html" /> <csrf disabled="true"/> </http> <beans:bean id="bcryptEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/> <authentication-manager> <authentication-provider user-service-ref="myUserAuth"> <password-encoder ref="bcryptEncoder"/> </authentication-provider> </authentication-manager> </beans:beans>定义自己的认证方式也就是自定义的认证方式，我这里使用的是MySQLpackage com.alc; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; 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 java.sql.*; import java.util.ArrayList; import java.util.List; public class auth implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { try { // 感觉最近有点手生，用一下原生的jdbc叭 Class.forName("com.mysql.jdbc.Driver"); // 创建链接 Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/studentmanager", "custom", "1.2.3.4.5.6"); // 写SQL语句 String sql = "SELECT * FROM user WHERE username = ?"; // 预处理对象 PreparedStatement preparedStatement = conn.prepareStatement(sql); // 给预处理对象赋值 还没记错，参数从1开始 噗哈哈哈 preparedStatement.setString(1, username); // 获取结果集 ResultSet resultSet = preparedStatement.executeQuery(); // 遍历结果集， while (resultSet.next()) { // 获取密码 String password = resultSet.getString("password"); // 创建认证列表 List<GrantedAuthority> authorities = new ArrayList<>(); // 简单的身份认证，给这个人定义一个权限 GrantedAuthority role; if (resultSet.getString("power").equals("管理员")) { role = new SimpleGrantedAuthority("ROLE_ADMIN"); } else { role = new SimpleGrantedAuthority("ROLE_USER"); } // 存入列表里 authorities.add(role); // 返回一个用户信息 // * @return a fully populated user record (never null) 来源于接口文档 // User(String username, String password, // Collection<? extends GrantedAuthority> authorities) // 上面为什么需要list也源于这里 return new User(username, password, authorities); } // 发生异常就交给虚拟机处理吧，没有找到这个用户就算了。。。 return null; } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); // 异常的话就更不用说了，数据库都炸了。。。 return null; } } }日志如下D:\Software\Java\jdk1.8.0_151\bin\java.exe -Dmaven.multiModuleProjectDirectory=E:\Workspace\train\security "-Dmaven.home=D:\Software\JetBrains\IntelliJ IDEA 2020.2.3\plugins\maven\lib\maven3" "-Dclassworlds.conf=D:\Software\JetBrains\IntelliJ IDEA 2020.2.3\plugins\maven\lib\maven3\bin\m2.conf" "-Dmaven.ext.class.path=D:\Software\JetBrains\IntelliJ IDEA 2020.2.3\plugins\maven\lib\maven-event-listener.jar" "-javaagent:D:\Software\JetBrains\IntelliJ IDEA 2020.2.3\lib\idea_rt.jar=5012:D:\Software\JetBrains\IntelliJ IDEA 2020.2.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Software\JetBrains\IntelliJ IDEA 2020.2.3\plugins\maven\lib\maven3\boot\plexus-classworlds-2.6.0.jar;D:\Software\JetBrains\IntelliJ IDEA 2020.2.3\plugins\maven\lib\maven3\boot\plexus-classworlds.license" org.codehaus.classworlds.Launcher -Didea.version=2020.2.3 -s D:\Software\apache-maven-3.6.3\conf\settings.xml -Dmaven.repo.local=D:\Software\apache-maven-3.6.3\repository tomcat7:run [INFO] Scanning for projects... [WARNING] [WARNING] Some problems were encountered while building the effective model for com.alc:security:war:1.0-SNAPSHOT [WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-compiler-plugin is missing. @ line 70, column 21 [WARNING] 'build.plugins.plugin.version' for org.apache.tomcat.maven:tomcat7-maven-plugin is missing. @ line 77, column 21 [WARNING] [WARNING] It is highly recommended to fix these problems because they threaten the stability of your build. [WARNING] [WARNING] For this reason, future Maven versions might no longer support building such malformed projects. [WARNING] [INFO] [INFO] --------------------------< com.alc:security >-------------------------- [INFO] Building security 1.0-SNAPSHOT [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] >>> tomcat7-maven-plugin:2.2:run (default-cli) > process-classes @ security >>> [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ security --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] Copying 1 resource [INFO] [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ security --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] <<< tomcat7-maven-plugin:2.2:run (default-cli) < process-classes @ security <<< [INFO] [INFO] [INFO] --- tomcat7-maven-plugin:2.2:run (default-cli) @ security --- [INFO] Running war on http://localhost:9090/ [INFO] Using existing Tomcat server configuration at E:\Workspace\train\security\target\tomcat [INFO] create webapp with contextPath: 五月 11, 2021 2:12:59 上午 org.apache.coyote.AbstractProtocol init 信息: Initializing ProtocolHandler ["http-bio-9090"] 五月 11, 2021 2:12:59 上午 org.apache.catalina.core.StandardService startInternal 信息: Starting service Tomcat 五月 11, 2021 2:12:59 上午 org.apache.catalina.core.StandardEngine startInternal 信息: Starting Servlet Engine: Apache Tomcat/7.0.47 五月 11, 2021 2:13:00 上午 org.apache.catalina.core.ApplicationContext log 信息: No Spring WebApplicationInitializer types detected on classpath 五月 11, 2021 2:13:00 上午 org.apache.catalina.core.ApplicationContext log 信息: Initializing Spring root WebApplicationContext 五月 11, 2021 2:13:00 上午 org.springframework.web.context.ContextLoader initWebApplicationContext 信息: Root WebApplicationContext: initialization started 五月 11, 2021 2:13:00 上午 org.springframework.web.context.support.XmlWebApplicationContext prepareRefresh 信息: Refreshing Root WebApplicationContext: startup date [Tue May 11 02:13:00 CST 2021]; root of context hierarchy 五月 11, 2021 2:13:00 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [spring-security.xml] 五月 11, 2021 2:13:00 上午 org.springframework.security.core.SpringSecurityCoreVersion performVersionChecks 信息: You are running with Spring Security Core 4.1.0.RELEASE 五月 11, 2021 2:13:00 上午 org.springframework.security.config.SecurityNamespaceHandler <init> 信息: Spring Security 'config' module version is 4.1.0.RELEASE 五月 11, 2021 2:13:01 上午 org.springframework.security.config.http.HttpSecurityBeanDefinitionParser checkFilterChainOrder 信息: Checking sorted filter chain: [Root bean: class [org.springframework.security.web.context.SecurityContextPersistenceFilter]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null, order = 200, Root bean: class [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null, order = 400, Root bean: class [org.springframework.security.web.header.HeaderWriterFilter]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null, order = 500, <org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#0>, order = 1100, Root bean: class [org.springframework.security.web.savedrequest.RequestCacheAwareFilter]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null, order = 1600, Root bean: class [org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null, order = 1700, Root bean: class [org.springframework.security.web.authentication.AnonymousAuthenticationFilter]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null, order = 2000, Root bean: class [org.springframework.security.web.session.SessionManagementFilter]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null, order = 2100, Root bean: class [org.springframework.security.web.access.ExceptionTranslationFilter]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null, order = 2200, <org.springframework.security.web.access.intercept.FilterSecurityInterceptor#0>, order = 2300] 五月 11, 2021 2:13:01 上午 org.springframework.security.web.DefaultSecurityFilterChain <init> 信息: Creating filter chain: Ant [pattern='/favicon.ico'], [] 五月 11, 2021 2:13:01 上午 org.springframework.security.web.DefaultSecurityFilterChain <init> 信息: Creating filter chain: Ant [pattern='/login.html'], [] 五月 11, 2021 2:13:01 上午 org.springframework.security.web.DefaultSecurityFilterChain <init> 信息: Creating filter chain: Ant [pattern='/registry.html'], [] 五月 11, 2021 2:13:01 上午 org.springframework.security.web.DefaultSecurityFilterChain <init> 信息: Creating filter chain: Ant [pattern='/admin/login.do'], [] 五月 11, 2021 2:13:01 上午 org.springframework.security.web.DefaultSecurityFilterChain <init> 信息: Creating filter chain: Ant [pattern='/css/*'], [] 五月 11, 2021 2:13:01 上午 org.springframework.security.web.DefaultSecurityFilterChain <init> 信息: Creating filter chain: Ant [pattern='/js/*'], [] 五月 11, 2021 2:13:01 上午 org.springframework.security.web.DefaultSecurityFilterChain <init> 信息: Creating filter chain: Ant [pattern='/imgs/*'], [] 五月 11, 2021 2:13:01 上午 org.springframework.security.web.DefaultSecurityFilterChain <init> 信息: Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher@1, [org.springframework.security.web.context.SecurityContextPersistenceFilter@b25cb42, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@66366fff, org.springframework.security.web.header.HeaderWriterFilter@4ce247a2, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@47bf8e75, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@6265221b, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@3779e03f, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@69787e25, org.springframework.security.web.session.SessionManagementFilter@520f9959, org.springframework.security.web.access.ExceptionTranslationFilter@19b10cf9, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@61b16fe8] 五月 11, 2021 2:13:01 上午 org.springframework.security.config.http.DefaultFilterChainValidator checkLoginPageIsntProtected 信息: Checking whether login URL '/login.html' is accessible with your configuration 五月 11, 2021 2:13:01 上午 org.springframework.web.context.ContextLoader initWebApplicationContext 信息: Root WebApplicationContext: initialization completed in 597 ms 五月 11, 2021 2:13:01 上午 org.apache.coyote.AbstractProtocol start 信息: Starting ProtocolHandler ["http-bio-9090"] Tue May 11 02:13:05 CST 2021 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Tue May 11 02:13:09 CST 2021 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.演示样例
CAS入门单点登录单点登录(Single Sign On)-->(SSO)，意思就是在一个应用上登录后，在其他应用上也保留登录状态。What's CASCAS是耶鲁大学发起的开源项目，巴拉巴拉巴拉~~，主要是为了解决单点登录的问题，大概流程是这样的。步骤访问服务：SSO客户端发送请求访问应用系统提供的服务资源定向认证：SSO客户端会重定向用户请求到SSO服务器用户认证：用户身份的认证发放令牌：SSO服务器产生令牌(Service Ticket)验证令牌：SSO服务器校验令牌(Service Ticket)的合法性，通过后允许客户端访问服务传输用户信息：SSO服务器验证令牌通过后，传输认证结果给客户端CAS服务器端部署CAS 服务端是war包，所以需要服务器的支撑，这里以Tomcat为例。这里以Windows平台为例，Tomcat部署到了本机把cas.war放到webapp下，启动tomcat。启动后访问http:/localhost:8080/cas/login此时你会看到登陆页面，默认的登录用户名和密码是固定的，casuser/Mellon修改默认用户名和密码找到cas/WEB-INF/deployerConfigContext.xml<bean id="primaryAuthenticationHandler" class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler"> <property name="users"> <entry key="casuser" value="Mellon" /> </property> </bean>这里我们发现，用户名和密码是成对存在的，并且写死了，对于部署真实项目是远远不能满足需求的，但是有没有发现，bean关键字，想到了Spring。那我们能不能使用MySQL呢？答案是可以的。从MySQL中认证用户这里我们可以尝试使用曾经的连接池和MySQL驱动，对接到CAS中，我们从上面的XML中可以看到，bean的id为primaryAuthenticationHandler，也就是主要的认证处理者，处理者是用户名和密码的键值对，那么我们由CAS的QueryDatabaseAuthenticationHandler进行对接就好了。对于认证我们的查询压力以及性能的的考虑，我们使用连接池进行操作。从上面的XML我呢见中，我们也可以发现，验证部分在deployerConfigContext.xml文件中，所以我们自己定义bean，并且让我们自己的bean来处理认证过程。<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" p:driverClass="com.mysql.jdbc.Driver" p:jdbcUrl="jdbc:mysql://127.0.0.1:3306/casDemo?characterEncoding=utf8" p:user="study" p:password="123456" /> <bean id="passwordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder" c:encodingAlgorithm="MD5" p:characterEncoding="UTF-8" /> <bean id="dbAuthHandler" class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler" p:dataSource-ref="dataSource" p:sql="select password from t_user where username = ?" p:passwordEncoder-ref="passwordEncoder" />这时，我们需要把默认的处理器换成我们自己的。dbAuthHandler<entry key-ref="dbAuthHandler" value-ref="primaryPrincipalResolver" />:warning: 注意，如果是连接其他机器，应该把数据库的远程访问权限打开(创建远程访问的账号[%] 或者针对某一个IP地址，具体情况具体操作)更改端口号如果开放了多个Web应用，端口冲突或者分布式情况，有修改端口号需求的，修改Tomcat的ip就不用说了，这里是需要修改CAS的配置，找到WEB-INF/cas.properties，修改server.name属性值，具体到tomcat的端口，也就是说要做到匹配。server.name=http://127.0.0.1:9100 server.prefix=${server.name}/casHTTPS认证HTTPS(http+ssl)也就是超文本传输协议+安全套接字==超文本传输安全协议，使连接更加安全，详细的说明可以参考维基百科(https://zh.wikipedia.org/wiki/超文本传输安全协议)但是在测试阶段，我们没有购买安全证书，虽然可以自己生成，但是为了方便，管理https的认证，如果不关闭认证，是无法进行登录的。修改cas的WEB-INF/deployerConfigContext.xml，找到如下部分代码，在下面需要添加p:requireSecure="false"。也就是需要安全置为假<bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler" p:httpClient-ref="httpClient" p:requireSecure="false"/>修改cas的/WEB-INF/spring-configuration/ticketGrantingTicketCookieGenerator.xml，把p:cookieSecure="true"改成false，但是cookieMaxAge为-1时也就是当前窗口有效，打开其他窗口就仍需要重新授权，所以时间也可以进行修改。<bean id="ticketGrantingTicketCookieGenerator" class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator" p:cookieSecure="false" p:cookieMaxAge="3600" p:cookieName="CASTGC" p:cookiePath="/cas" />修改cas的WEB-INF/spring-configuration/warnCookieGenerator.xml，同上<bean id="warnCookieGenerator" class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator" p:cookieSecure="false" p:cookieMaxAge="3600" p:cookieName="CASPRIVACY" p:cookiePath="/cas" />CAS客户端的搭建依旧是Maven工程，web项目所以使用war进行打包，引入CAS客户端依赖和Tomcat的依赖。<packaging>war</packaging> <properties> <webVersion>3.0</webVersion> </properties> <dependencies> <dependency> <groupId>org.jasig.cas.client</groupId> <artifactId>cas-client-core</artifactId> <version>3.3.3</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> 1.8 <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <configuration> <port>9001</port> <path>/</path> </configuration> </plugin> </plugins> </build>web.xml<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <filter> <filter-name>CASFilter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>http://127.0.0.1:9100/cas/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://localhost:9001</param-value> </init-param> </filter> <filter-mapping> <filter-name>CASFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>casValidationFilter</filter-name> <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>http://127.0.0.1:9100/cas</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://localhost:9001</param-value> </init-param> </filter> <filter-mapping> <filter-name>casValidationFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>CasHttpRequestFilter</filter-name> <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class> </filter> <filter-mapping> <filter-name>CasHttpRequestFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>casLocalFilter</filter-name> <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class> </filter> <filter-mapping> <filter-name>casLocalFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <filter> <filter-name>casSingOutFilter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> </filter> <filter-mapping> <filter-name>casSingOutFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>接下来运行这个客户端，发现是需要登录的，当我们创建一个客户端副本，改下端口，运行两个客户端，一个客户端登陆后，第二个客户端就不需要再次登录了。也就是单点登录的特性。附加戏登出处理，可以加一个a标签，或者访问地址http://127.0.0.1:9000/cas/logout如果想要退出后自动跳转到一个地址，需要修改一下配置。找到cas-servlet.xml，进行如下修改，把followServiceRedirects改为true<bean id="logoutAction" class="org.jasig.cas.web.flow.LogoutAction" p:servicesManager-ref="servicesManager" p:followServiceRedirects="${cas.logout.followServiceRedirects:true}"/>此时，可以将地址改为这样。http://127.0.0.1:9100/cas/logout?service=http://www.zunmx.top修改默认页面找到WEB-INF\view\jsp\default\ui\casLoginView.jsp，这个文件就是默认的登陆页面，进行修改就行了，但是需要注意的是，要遵循它的规则，比如用户名和密码输入框的属性以及jsp的规则。头部属性添加<%@ page pageEncoding="UTF-8" %> <%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>比如下面的代码，遵循原生的模板<form:form method="post" id="fm1" commandName="${commandName}" htmlEscape="true" class="sui-form"> <form:input id="username" tabindex="1" accesskey="${userNameAccessKey}" path="username" autocomplete="off" htmlEscape="true" placeholder="邮箱/用户名/手机号" class="span2 input-xfat" /> <form:password id="password" tabindex="2" path="password" accesskey="${passwordAccessKey}" htmlEscape="true" autocomplete="off" placeholder="请输入密码" class="span2 input-xfat" /> </form:form>错误提示部分<form:errors path="*" id="msg" cssClass="errors" element="div" htmlEscape="false" />接下来修改默认的语言(I18N)，找到WEB-INF\classes，CAS提供了国际化的语言配置文件，简体中文的文件是messages_zh_CN.properties，但是缺少了一些说明，我们补一下。authenticationFailure.AccountNotFoundException=\u7528\u6237\u4E0D\u5B58\u5728. authenticationFailure.FailedLoginException=\u5BC6\u7801\u9519\u8BEF.然后修改默认的语言，找到cas-servlet.xml文件，修改如下。<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver" p:defaultLocale="zh_CN" />集成SpringSecurity导入Maven依赖<?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> <groupId>com.alc</groupId> <artifactId>casClient_demo3</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <webVersion>3.0</webVersion> <spring.version>4.2.4.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-cas</artifactId> <version>4.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.jasig.cas.client</groupId> <artifactId>cas-client-core</artifactId> <version>3.3.3</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-cas</artifactId> <version>4.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.jasig.cas.client</groupId> <artifactId>cas-client-core</artifactId> <version>3.3.3</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>4.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>4.1.0.RELEASE</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> 1.8 <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <configuration> <path>/</path> <port>9003</port> </configuration> </plugin> </plugins> </build> </project>配置web.xml<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-security.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>配置spring-security<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <http pattern="/index2.html" security="none"/> <http use-expressions="false" entry-point-ref="casProcessingFilterEntryPoint"> <intercept-url pattern="/**" access="ROLE_USER"/> <csrf disabled="true"/> <custom-filter ref="casAuthenticationFilter" position="CAS_FILTER"/> <custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER"/> <custom-filter ref="singleLogoutFilter" before="CAS_FILTER"/> </http> <beans:bean id="casProcessingFilterEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint"> <beans:property name="loginUrl" value="http://127.0.0.1:9100/cas/login"/> <beans:property name="serviceProperties" ref="serviceProperties"/> </beans:bean> <beans:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties"> <beans:property name="service" value="http://localhost:9003/login/cas"/> </beans:bean> <beans:bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter"> <beans:property name="authenticationManager" ref="authenticationManager"/> </beans:bean> <authentication-manager alias="authenticationManager"> <authentication-provider ref="casAuthenticationProvider"/> </authentication-manager> <beans:bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider"> <beans:property name="authenticationUserDetailsService"> <beans:bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper"> <beans:constructor-arg ref="userDetailsService"/> </beans:bean> </beans:property> <beans:property name="serviceProperties" ref="serviceProperties"/> <beans:property name="ticketValidator"> <beans:bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator"> <beans:constructor-arg name="casServerUrlPrefix" value="http://127.0.0.1:9100/cas"/> </beans:bean> </beans:property> <beans:property name="key" value="an_id_for_this_auth_provider_only"/> </beans:bean> <beans:bean id="userDetailsService" class="com.alc.demo.service.UserDetailServiceImpl"/> <beans:bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/> <beans:bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter"> <beans:constructor-arg value="http://127.0.0.1:9100/cas/logout?service=http://www.zunmx.top"/> <beans:constructor-arg> <beans:bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/> </beans:constructor-arg> <beans:property name="filterProcessesUrl" value="/logout/cas"/> </beans:bean> </beans:beans>配置springmvc<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.alc.demo" /> <mvc:annotation-driven /> </beans>配置认证时使用的用户服务（UserDetailsService）package com.alc.demo.service; 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 java.util.ArrayList; import java.util.List; public class UserDetailServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { List<GrantedAuthority> authorities=new ArrayList(); authorities.add(new SimpleGrantedAuthority("ROLE_USER")); return new User(username, "" , authorities); } }因为授权认证由CAS做了，所以Spring-Security赋予角色就够了。配置controller层package com.alc.demo.controller; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @RequestMapping("/findLoginUser") public void findLoginUser(){ String name = SecurityContextHolder.getContext().getAuthentication().getName(); System.out.println(name); } }定义默认的页面<%-- Created by IntelliJ IDEA. User: 36083 Date: 2021/04/13 Time: 10:37 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> Title success 退出登录