溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring Boot整合Spring Security如何實現(xiàn)登入登出功能

發(fā)布時間:2021-05-31 13:50:08 來源:億速云 閱讀:328 作者:小新 欄目:編程語言

這篇文章將為大家詳細講解有關(guān)Spring Boot整合Spring Security如何實現(xiàn)登入登出功能,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

方法如下:

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實現(xiàn)簡單登入登出</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 . 準備你的數(shù)據(jù)庫,設(shè)計表結(jié)構(gòu),要用戶使用登入登出,新建用戶表。

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 . 考慮我們應(yīng)用的效率 , 可以配置數(shù)據(jù)源和線程池 :

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);//線程池維護線程的最少數(shù)量
 executor.setMaxPoolSize(30);//線程池維護線程的最大數(shù)量
 executor.setQueueCapacity(8); //緩存隊列
 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //對拒絕task的處理策略
 executor.setKeepAliveSeconds(60);//允許的空閑時間
 executor.initialize();
 return executor;
 }
}

6.用戶需要根據(jù)用戶名進行登錄,訪問數(shù)據(jù)庫 :

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.構(gòu)建真正用于SpringSecurity登錄的安全用戶(UserDetails),我這里使用新建了一個POJO來實現(xiàn) :

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() { //用戶登錄實現(xiàn)
 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.至此,已經(jīng)基本將配置搭建好了,從上面核心可以看出,配置的登錄頁的url 為/login,可以創(chuàng)建基本的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.根據(jù)Freemark和Controller里面可看出配置的視圖為 /templates/index.html和/templates/index.login。所以創(chuàng)建基本的登錄頁面和登錄成功頁面。

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中獲取到登錄的用戶信息,根據(jù)配置SpringSecurity的用戶信息會放在Session.SPRING_SECURITY_CONTEXT.authentication.principal里面,根據(jù)FreeMarker模板引擎的特點,可以通過這種方式進行獲取 : <#assign user=Session.SPRING_SECURITY_CONTEXT.authentication.principal/>

12 . 為了方便測試,我們在數(shù)據(jù)庫中插入一條記錄,注意,從WebSecurity.java配置可以知道密碼會被加密,所以我們插入的用戶密碼應(yīng)該是被加密的。

這里假如我們使用的密碼為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);
 }
}

測試登錄,從上面的加密的密碼我們插入一條數(shù)據(jù)到數(shù)據(jù)庫中。

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

 Spring Boot整合Spring Security如何實現(xiàn)登入登出功能

點擊登錄,登錄失敗會留在當前頁面重新登錄,成功則進入index.html

 登錄如果成功,可以看到后臺打印登錄成功的日志 :

Spring Boot整合Spring Security如何實現(xiàn)登入登出功能

 頁面進入index.html :

Spring Boot整合Spring Security如何實現(xiàn)登入登出功能

點擊注銷 ,則回重新跳轉(zhuǎn)到login.html,后臺也會打印登出成功的日志 :

Spring Boot整合Spring Security如何實現(xiàn)登入登出功能

關(guān)于“Spring Boot整合Spring Security如何實現(xiàn)登入登出功能”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI