您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Spring Boot整合Spring Security如何實現登入登出功能,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
方法如下:
1 . 新建一個spring-security-login的maven項目 ,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.wuxicloud</groupId> <artifactId>spring-security-login</artifactId> <version>1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.6.RELEASE</version> </parent> <properties> <author>EalenXie</author> <description>SpringBoot整合SpringSecurity實現簡單登入登出</description> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</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-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</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-freemarker</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!--alibaba--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.24</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.31</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> </dependencies> </project>
2 . 準備你的數據庫,設計表結構,要用戶使用登入登出,新建用戶表。
DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_uuid` varchar(70) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `telephone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `role` int(10) DEFAULT NULL, `image` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `last_ip` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `last_time` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; SET FOREIGN_KEY_CHECKS = 1;
3 . 用戶對象User.java :
import javax.persistence.*; /** * Created by EalenXie on 2018/7/5 15:17 */ @Entity @Table(name = "USER") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; private String user_uuid; //用戶UUID private String username; //用戶名 private String password; //用戶密碼 private String email; //用戶郵箱 private String telephone; //電話號碼 private String role; //用戶角色 private String image; //用戶頭像 private String last_ip; //上次登錄IP private String last_time; //上次登錄時間 public Integer getId() { return id; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } public String getImage() { return image; } public void setImage(String image) { this.image = image; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getTelephone() { return telephone; } public void setTelephone(String telephone) { this.telephone = telephone; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUser_uuid() { return user_uuid; } public void setUser_uuid(String user_uuid) { this.user_uuid = user_uuid; } public String getLast_ip() { return last_ip; } public void setLast_ip(String last_ip) { this.last_ip = last_ip; } public String getLast_time() { return last_time; } public void setLast_time(String last_time) { this.last_time = last_time; } @Override public String toString() { return "User{" + "id=" + id + ", user_uuid='" + user_uuid + '\'' + ", username='" + username + '\'' + ", password='" + password + '\'' + ", email='" + email + '\'' + ", telephone='" + telephone + '\'' + ", role='" + role + '\'' + ", image='" + image + '\'' + ", last_ip='" + last_ip + '\'' + ", last_time='" + last_time + '\'' + '}'; } }
4 . application.yml配置一些基本屬性
spring: resources: static-locations: classpath:/ freemarker: template-loader-path: classpath:/templates/ suffix: .html content-type: text/html charset: UTF-8 datasource: url: jdbc:mysql://localhost:3306/yourdatabase username: yourname password: yourpass driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource server: port: 8083 error: whitelabel: enabled: true
5 . 考慮我們應用的效率 , 可以配置數據源和線程池 :
package com.wuxicloud.config; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidDataSourceFactory; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.*; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; import java.util.Properties; @Configuration public class DruidConfig { private static final String DB_PREFIX = "spring.datasource."; @Autowired private Environment environment; @Bean @ConfigurationProperties(prefix = DB_PREFIX) public DataSource druidDataSource() { Properties dbProperties = new Properties(); Map<String, Object> map = new HashMap<>(); for (PropertySource<?> propertySource : ((AbstractEnvironment) environment).getPropertySources()) { getPropertiesFromSource(propertySource, map); } dbProperties.putAll(map); DruidDataSource dds; try { dds = (DruidDataSource) DruidDataSourceFactory.createDataSource(dbProperties); dds.init(); } catch (Exception e) { throw new RuntimeException("load datasource error, dbProperties is :" + dbProperties, e); } return dds; } private void getPropertiesFromSource(PropertySource<?> propertySource, Map<String, Object> map) { if (propertySource instanceof MapPropertySource) { for (String key : ((MapPropertySource) propertySource).getPropertyNames()) { if (key.startsWith(DB_PREFIX)) map.put(key.replaceFirst(DB_PREFIX, ""), propertySource.getProperty(key)); else if (key.startsWith(DB_PREFIX)) map.put(key.replaceFirst(DB_PREFIX, ""), propertySource.getProperty(key)); } } if (propertySource instanceof CompositePropertySource) { for (PropertySource<?> s : ((CompositePropertySource) propertySource).getPropertySources()) { getPropertiesFromSource(s, map); } } } @Bean public ServletRegistrationBean druidServlet() { return new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); } @Bean public FilterRegistrationBean filterRegistrationBean() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new WebStatFilter()); filterRegistrationBean.addUrlPatterns("/*"); filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); return filterRegistrationBean; } }
配置線程池 :
package com.wuxicloud.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; @Configuration @EnableAsync public class ThreadPoolConfig { @Bean public Executor getExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5);//線程池維護線程的最少數量 executor.setMaxPoolSize(30);//線程池維護線程的最大數量 executor.setQueueCapacity(8); //緩存隊列 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //對拒絕task的處理策略 executor.setKeepAliveSeconds(60);//允許的空閑時間 executor.initialize(); return executor; } }
6.用戶需要根據用戶名進行登錄,訪問數據庫 :
import com.wuxicloud.model.User; import org.springframework.data.jpa.repository.JpaRepository; /** * Created by EalenXie on 2018/7/11 14:23 */ public interface UserRepository extends JpaRepository<User, Integer> { User findByUsername(String username); }
7.構建真正用于SpringSecurity登錄的安全用戶(UserDetails),我這里使用新建了一個POJO來實現 :
package com.wuxicloud.security; import com.wuxicloud.model.User; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.ArrayList; import java.util.Collection; public class SecurityUser extends User implements UserDetails { private static final long serialVersionUID = 1L; public SecurityUser(User user) { if (user != null) { this.setUser_uuid(user.getUser_uuid()); this.setUsername(user.getUsername()); this.setPassword(user.getPassword()); this.setEmail(user.getEmail()); this.setTelephone(user.getTelephone()); this.setRole(user.getRole()); this.setImage(user.getImage()); this.setLast_ip(user.getLast_ip()); this.setLast_time(user.getLast_time()); } } @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> authorities = new ArrayList<>(); String username = this.getUsername(); if (username != null) { SimpleGrantedAuthority authority = new SimpleGrantedAuthority(username); authorities.add(authority); } return authorities; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
8 . 核心配置,配置SpringSecurity訪問策略,包括登錄處理,登出處理,資源訪問,密碼基本加密。
package com.wuxicloud.config; import com.wuxicloud.dao.UserRepository; import com.wuxicloud.model.User; import com.wuxicloud.security.SecurityUser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; 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.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.Authentication; 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.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * Created by EalenXie on 2018/1/11. */ @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private static final Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class); @Override protected void configure(HttpSecurity http) throws Exception { //配置策略 http.csrf().disable(); http.authorizeRequests(). antMatchers("/static/**").permitAll().anyRequest().authenticated(). and().formLogin().loginPage("/login").permitAll().successHandler(loginSuccessHandler()). and().logout().permitAll().invalidateHttpSession(true). deleteCookies("JSESSIONID").logoutSuccessHandler(logoutSuccessHandler()). and().sessionManagement().maximumSessions(10).expiredUrl("/login"); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder()); auth.eraseCredentials(false); } @Bean public BCryptPasswordEncoder passwordEncoder() { //密碼加密 return new BCryptPasswordEncoder(4); } @Bean public LogoutSuccessHandler logoutSuccessHandler() { //登出處理 return new LogoutSuccessHandler() { @Override public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { try { SecurityUser user = (SecurityUser) authentication.getPrincipal(); logger.info("USER : " + user.getUsername() + " LOGOUT SUCCESS ! "); } catch (Exception e) { logger.info("LOGOUT EXCEPTION , e : " + e.getMessage()); } httpServletResponse.sendRedirect("/login"); } }; } @Bean public SavedRequestAwareAuthenticationSuccessHandler loginSuccessHandler() { //登入處理 return new SavedRequestAwareAuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { User userDetails = (User) authentication.getPrincipal(); logger.info("USER : " + userDetails.getUsername() + " LOGIN SUCCESS ! "); super.onAuthenticationSuccess(request, response, authentication); } }; } @Bean public UserDetailsService userDetailsService() { //用戶登錄實現 return new UserDetailsService() { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { User user = userRepository.findByUsername(s); if (user == null) throw new UsernameNotFoundException("Username " + s + " not found"); return new SecurityUser(user); } }; } }
9.至此,已經基本將配置搭建好了,從上面核心可以看出,配置的登錄頁的url 為/login,可以創建基本的Controller來驗證登錄了。
package com.wuxicloud.web; import com.wuxicloud.model.User; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; /** * Created by EalenXie on 2018/1/11. */ @Controller public class LoginController { @RequestMapping(value = "/login", method = RequestMethod.GET) public String login() { return "login"; } @RequestMapping("/") public String root() { return "index"; } public User getUser() { //為了session從獲取用戶信息,可以配置如下 User user = new User(); SecurityContext ctx = SecurityContextHolder.getContext(); Authentication auth = ctx.getAuthentication(); if (auth.getPrincipal() instanceof UserDetails) user = (User) auth.getPrincipal(); return user; } public HttpServletRequest getRequest() { return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); } }
11 . SpringBoot基本的啟動類 Application.class
package com.wuxicloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * Created by EalenXie on 2018/7/11 15:01 */ @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
11.根據Freemark和Controller里面可看出配置的視圖為 /templates/index.html和/templates/index.login。所以創建基本的登錄頁面和登錄成功頁面。
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用戶登錄</title> </head> <body> <form action="/login" method="post"> 用戶名 : <input type="text" name="username"/> 密碼 : <input type="password" name="password"/> <input type="submit" value="登錄"> </form> </body> </html>
注意 : 這里方法必須是POST,因為GET在controller被重寫了,用戶名的name屬性必須是username,密碼的name屬性必須是password
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>首頁</title> <#assign user=Session.SPRING_SECURITY_CONTEXT.authentication.principal/> </head> <body> 歡迎你,${user.username}<br/> <a href="/logout">注銷</a> </body> </html>
注意 : 為了從session中獲取到登錄的用戶信息,根據配置SpringSecurity的用戶信息會放在Session.SPRING_SECURITY_CONTEXT.authentication.principal里面,根據FreeMarker模板引擎的特點,可以通過這種方式進行獲取 : <#assign user=Session.SPRING_SECURITY_CONTEXT.authentication.principal/>
12 . 為了方便測試,我們在數據庫中插入一條記錄,注意,從WebSecurity.java配置可以知道密碼會被加密,所以我們插入的用戶密碼應該是被加密的。
這里假如我們使用的密碼為admin,則加密過后的字符串是 $2a$04$1OiUa3yEchBXQBJI8JaMyuKZNlwzWvfeQjKAHnwAEQwnacjt6ukqu
測試類如下 :
package com.wuxicloud.security; import org.junit.Test; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; /** * Created by EalenXie on 2018/7/11 15:13 */ public class TestEncoder { @Test public void encoder() { String password = "admin"; BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(4); String enPassword = encoder.encode(password); System.out.println(enPassword); } }
測試登錄,從上面的加密的密碼我們插入一條數據到數據庫中。
INSERT INTO `USER` VALUES (1, 'd242ae49-4734-411e-8c8d-d2b09e87c3c8', 'EalenXie', '$2a$04$petEXpgcLKfdLN4TYFxK0u8ryAzmZDHLASWLX/XXm8hgQar1C892W', 'SSSSS', 'ssssssssss', 1, 'g', '0:0:0:0:0:0:0:1', '2018-07-11 11:26:27');
13 . 啟動項目進行測試 ,訪問 localhost:8083
點擊登錄,登錄失敗會留在當前頁面重新登錄,成功則進入index.html
登錄如果成功,可以看到后臺打印登錄成功的日志 :
頁面進入index.html :
點擊注銷 ,則回重新跳轉到login.html,后臺也會打印登出成功的日志 :
關于“Spring Boot整合Spring Security如何實現登入登出功能”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。