Spring Security+Spring MVC+Mybatis
项目环境:JDK8+maven3.0+MySQL
项目结构:
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> <groupId>com.liby</groupId> <artifactId>springsecurity</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>springsecurity Maven Webapp</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> <springframework.version>4.2.5.RELEASE</springframework.version> <springsecurity.version>4.0.4.RELEASE</springsecurity.version> <mysql.connector.version>5.1.31</mysql.connector.version> <mybatis.version>3.2.6</mybatis.version> <!-- log4j日志文件管理包版本 --> <slf4j.version>1.7.7</slf4j.version> <log4j.version>1.2.17</log4j.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!-- Spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${springframework.version}</version> </dependency> <!-- Spring Security --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${springsecurity.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${springsecurity.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>${springsecurity.version}</version> </dependency> <!-- MySQL --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.connector.version}</version> </dependency> <!-- Servlet+JSP+JSTL --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- mybatis核心包 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.2</version> </dependency> <!-- mybatis/spring包 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.2.2</version> </dependency> <!-- log start --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <!-- 格式化对象,方便输出日志 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <!-- log end --> <!-- 加载数据源 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.18</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.31</version> </dependency> </dependencies> <build> <!--解决Intellij构建项目时,target/classes目录下不存在mapper.xml文件--> <resources> <resource> <directory>${basedir}/src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources> <finalName>springsecurity</finalName> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.2</version> <configuration> <configurationFile>src/main/resources/mybatis-generator-config.xml</configurationFile> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> <executions> <execution> <id>Generate MyBatis Artifacts</id> <goals> <goal>generate</goal> </goals> </execution> </executions> </plugin> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.0.0</version> </plugin> <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.20.1</version> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.0</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> </plugins> </pluginManagement> </build> </project>
web.xml:
<web-app> <display-name>Archetype Created Web Application</display-name> <!-- Spring监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 防止Spring内存溢出监听器 --> <listener> <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class> </listener> <!-- Spring MVC servlet --> <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> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <!-- 此处可以可以配置成*.do,对应struts的后缀习惯 --> <url-pattern>/</url-pattern> </servlet-mapping> <!-- Spring的配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-*.xml</param-value> </context-param> <!--springSecurity --> <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> <!-- 编码过滤器 --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!--欢迎页面--> <welcome-file-list> <welcome-file>/index.html</welcome-file> <welcome-file>/index.jsp</welcome-file> </welcome-file-list> </web-app>
db.properties:
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/springsecurity?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true name=root password=Yy30240103
log4j.properties:
### 设置### log4j.rootLogger = debug,stdout,D,E ### 输出信息到控制抬 ### log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n ### 输出DEBUG 级别以上的日志到文件F://logs/debug.log ### log4j.appender.D = org.apache.log4j.FileAppender log4j.appender.D.File = E:/logs/debug.log log4j.appender.D.Append = true log4j.appender.D.Threshold = DEBUG log4j.appender.D.layout = org.apache.log4j.PatternLayout log4j.appender.D.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n ### 输出ERROR 级别以上的日志到文件F://logs/error.log ### log4j.appender.E = org.apache.log4j.FileAppender log4j.appender.E.File = E://logs/error.log log4j.appender.E.Append = true log4j.appender.E.Threshold = ERROR log4j.appender.E.layout = org.apache.log4j.PatternLayout log4j.appender.E.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
spring-dao.xml:
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="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-4.0.xsd"> <!-- 加载配置文件 --> <context:property-placeholder location="classpath:db.properties" ignore-unresolvable="true" /> <!-- 数据库连接池 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="getConnection" destroy-method="close"> <property name="driverClassName" value="${driver}" /> <property name="url" value="${url}" /> <property name="username" value="${name}" /> <property name="password" value="${password}" /> <!-- 初始化连接大小 --> <property name="initialSize" value="2" /> <!-- 连接池最大使用连接数量 --> <property name="maxActive" value="100" /> <!-- 连接池最小空闲 --> <property name="minIdle" value="2" /> <!-- 获取连接最大等待时间 --> <!-- <property name="maxWait" value="60000" /> --> </bean> <!-- mapper配置 --> <!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 --> <!-- mybatis核心bean --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 数据库连接池 --> <property name="dataSource" ref="dataSource" /> <!-- 加载mybatis的全局配置文件 --> <property name="configLocation" value="classpath:SqlMapConfig.xml" /> <property name="mapperLocations" value="classpath:com/liby/springmvc/mapper/**/*.xml"/> </bean> <!-- 配置Mapper扫描器 --> <!-- 自动扫描DAO接口 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.liby.springmvc.dao"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean> <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
spring-mvc.xml:
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="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-4.0.xsd"> <!-- 激活spring的注解. --> <context:annotation-config /> <!-- @Controller注解扫描 --> <context:component-scan base-package="com.liby.springmvc"></context:component-scan> <!-- 配置视图解析器 作用:在controller中指定页面路径的时候就不用写页面的完整路径名称了,可以直接写页面去掉扩展名的名称 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 真正的页面路径 = 前缀 + 去掉后缀名的页面名称 + 后缀 --> <!-- 前缀 --> <property name="prefix" value="/view/"></property> <!-- 后缀 --> <property name="suffix" value=".jsp"></property> </bean> </beans>
SqlMapConfig.xml:mybatis配置文件,不可缺少。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> </configuration>
重点来啦!!!
Spring Security配置
spring-security.xml
<?xml version="1.0" encoding="UTF-8"?> <!--suppress ALL --> <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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="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-4.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <context:component-scan base-package="com.liby.springmvc"></context:component-scan> <http pattern="/login" security="none"/> <http auto-config="true"> <!--intercept-url有拦截顺序,如果下面两个被反转了./**会一直 被匹配,/security/admin**就永远也不会执行。--> <!--满足该条件的请求需要有ADMIN角色--> <intercept-url pattern="/security/admin**" access="hasRole('ROLE_ADMIN')"/> <!--pattern="/**" 对所有路径进行角色认证--> <intercept-url pattern="/**" access="hasRole('ROLE_USER')"/> <!--自定义登录页面是通过login-page属性来指定的。提到login-page我们不得不提另外几个属性。 1、username-parameter:表示登录时用户名使用的是哪个参数,默认是“j_username”。 2、password-parameter:表示登录时密码使用的是哪个参数,默认是“j_password”。 3、login-processing-url:表示登录时提交的地址,默认是“/j-spring-security-check”。这个只是Spring Security用来标记登录页面使用的提交地址,真正关于登录这个请求是不需要用户自己处理的。 4、authentication-success-handler-ref:使用了authentication-success-handler-ref之后认证成功后的处理就由指定的AuthenticationSuccessHandler来处理,default-target-url则失效 5、authentication-failure-url:通过authentication-failure-url指定登录失败后的页面 6、authentication-failure-handler-ref:对应一个用于处理认证失败的AuthenticationFailureHandler实现类。指定了该属性,Spring Security在认证失败后会调用指定AuthenticationFailureHandler的onAuthenticationFailure方法对认证失败进行处理,此时authentication-failure-url属性将不再发生作用。 --> <form-login login-page="/login" login-processing-url="/checkLogin" default-target-url="/hello" authentication-success-handler-ref="authenticationSuccessHandlerImpl" username-parameter="username" password-parameter="password" authentication-failure-url="/error" /> <!--要实现退出登录的功能我们需要在http元素下定义logout元素,这样Spring Security将自动为我们添加用于处理退出登录的过滤器LogoutFilter到FilterChain。 当我们指定了http元素的auto-config属性为true时logout定义是会自动配置的,此时我们默认退出登录的URL为“/logout”, 可以通过logout元素的logout-url属性来改变退出登录的默认地址。 1、logout-url:改变退出登录的默认地址,这里需要注意的一点是,spring security 3.x默认的注销拦截url为/j_spring_security_logout,而4.x则默认使用/logout 2、invalidate-session:表示是否要在退出登录后让当前session失效,默认为true。 3、delete-cookies:指定退出登录后需要删除的cookie名称,多个cookie之间以逗号分隔。 4、logout-success-url:指定成功退出登录后要重定向的URL。需要注意的是对应的URL应当是不需要登录就可以访问的。 5、success-handler-ref:指定用来处理成功退出登录的LogoutSuccessHandler的引用。 --> <logout invalidate-session="true" logout-success-url="/login" /> <!--Spring Security 4默认启用了CSRF保护功能(false),该功能在Spring Security 3时就已经存在默认是不启用,该功能防止跨站请求伪造攻击; 在提交请求时,该请求被CsrfFilter拦截,验证_csrf的token是否有效。 --> <csrf disabled="true"/> </http> <!--<authentication-manager>--> <!--<authentication-provider>--> <!--<user-service>--> <!--<user name="yebing" password="123456" authorities="ROLE_USER"/>--> <!--<user name="admin" password="admin" authorities="ROLE_USER,ROLE_ADMIN"/>--> <!--</user-service>--> <!--</authentication-provider>--> <!--</authentication-manager>--> <!-- 配置认证管理器 --> <authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="userDetailServiceImpl"> <!--<password-encoder hash="md5">--> <!--<salt-source user-property="lamb" />--> <!--</password-encoder>--> <password-encoder ref="messageDigestPasswordEncoderImpl"></password-encoder> </authentication-provider> </authentication-manager> <beans:bean id="messageDigestPasswordEncoderImpl" class="com.liby.springmvc.config.springsecurity.MessageDigestPasswordEncoderImpl"> <beans:constructor-arg value="md5"></beans:constructor-arg> </beans:bean> </beans:beans>
Java代码:
MD5加密工具类:Md5Util.java
package com.li.springmvc.util; import org.springframework.stereotype.Component; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * 功能:获取Md5加密后的密文 * @Author Created by yebing * @Date 2018/8/11 22:07 * @Version 1.0.0 */ @Component public class Md5Util { private static final String SALT = "lamb"; public String encode(String password){ password = password + SALT; StringBuffer stringBuffer = new StringBuffer(); MessageDigest digest = null; try { digest = MessageDigest.getInstance("md5"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } byte[] strByte = password.getBytes(); byte[] result = digest.digest(strByte); System.out.println(); for (byte aByte : result) { String s=Integer.toHexString(0xff & aByte); if(s.length()==1){ stringBuffer.append(s); }else{ stringBuffer.append(s); } } return stringBuffer.toString(); } public static void main(String[] args){ Md5Util md5Util = new Md5Util(); System.out.println(md5Util.encode("123456")); } }
AuthenticationSuccessHandlerImpl.java:
用作Spring Security认证成功后的处理,可用作页面跳转等逻辑业务处理。
package com.li.springmvc.config.springsecurity; import org.springframework.context.annotation.Bean; import org.springframework.security.core.Authentication; 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; /** * 功能:Spring Security成功登陆后页面处理器, * 结合Spring-security.xml文件下authentication-success-handler-ref="authenticationSuccessHandlerImpl"使用 * * @Author Created by yebing * @Date 2018/8/12 21:15 * @Version 1.0.0 */ @Component("authenticationSuccessHandlerImpl") public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { System.out.println("Setting session!"); httpServletResponse.sendRedirect("/hello"); } }
UserDetailsServiceImpl.java:
用于登录时,获取前端发送过来的账号,调用dao动态获取数据库用户信息,例如账号权限以及密码,并将账号信息写入Spring Security中。
package com.li.springmvc.config.springsecurity; import com.li.springmvc.dao.UserDao; import com.li.springmvc.domain.UserBean; import com.li.springmvc.util.Md5Util; import org.apache.log4j.Logger; 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 java.util.ArrayList; import java.util.List; /** * 功能:动态获取用户账号密码认证 * @Author Created by yebing * @Date 2018/8/12 22:05 * @Version 1.0.0 */ @Component("userDetailServiceImpl") public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private UserDao userDao; @Autowired private Md5Util md5Util; private static Logger logger = Logger.getLogger(UserDetailsServiceImpl.class); @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { UserBean userBean = userDao.getUser(username); if (null == userBean) { throw new UsernameNotFoundException("用户" + username + "不存在"); } //GrantedAuthority是security提供的权限类, List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>(); //获取角色,放到list里面 getRoles(userBean,auths); logger.info("数据库密码:"+userBean.getPassword()); //返回包括权限角色的User给security return new User(username, userBean.getPassword(), true, true, true, true, auths); } public void getRoles(UserBean userBean,List<GrantedAuthority> list){ for (String role:userBean.getRoles().split(",")) { //权限如果前缀是ROLE_,security就会认为这是个角色信息,而不是权限,例如ROLE_MENBER就是MENBER角色,CAN_SEND就是CAN_SEND权限 System.out.println("角色是:"+role); list.add(new SimpleGrantedAuthority("ROLE_"+role)); } }
MessageDigestPasswordEncoderImpl.java:
密码认证处理器,继承MessageDigestPasswordEncoder类,重写密码认证方法。
package com.li.springmvc.config.springsecurity; import com.li.springmvc.util.Md5Util; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.encoding.MessageDigestPasswordEncoder; import org.springframework.stereotype.Component; /** * 功能:密码认证处理器,继承MessageDigestPasswordEncoder类,重写密码认证方法。 * <password-encoder ref="messageDigestPasswordEncoderImpl"></password-encoder>搭配使用 * @Author: yebing * @Date: 2018-8-13 12:32 * @Version 1.0.0 */ public class MessageDigestPasswordEncoderImpl extends MessageDigestPasswordEncoder { @Autowired private Md5Util md5Util; private static Logger logger = Logger.getLogger(MessageDigestPasswordEncoderImpl.class); public MessageDigestPasswordEncoderImpl(String algorithm) throws IllegalArgumentException{ super(algorithm); } public MessageDigestPasswordEncoderImpl(String algorithm, boolean encodeHashAsBase64) throws IllegalArgumentException { super(algorithm, encodeHashAsBase64); } /** * @param encPass 数据库密码 * @param rawPass 前端传送过来的密码 * @param salt 加盐,这里忽略,暂时不引用 * @return */ @Override public boolean isPasswordValid(String encPass, String rawPass, Object salt) { String pass1 = "" + encPass; String pass2 = md5Util.encode(rawPass); boolean bool=false; if(pass1.equals(pass2)){ bool=true; } return bool; } }
最后来一个Spring Security注销登录:
@RequestMapping(value = "/logout") public String logoutPage(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){ Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if(authentication!=null){ new SecurityContextLogoutHandler().logout(httpServletRequest,httpServletResponse,authentication); } logger.info("注销登录成功!"); return "login"; }
就这么简单粗暴的注销实例。
项目github地址:完整的项目,不懂的可以看代码注释,我觉得我写的还是比较清楚的