SpringSecurity Web 權限方案


1 設置登錄的用戶名和密碼

1.1 通過配置文件

  • application.properties
server.port=8181

# 設置登錄的用戶名和密碼
spring.security.user.name=admin
spring.security.user.password=123456

1.2 通過配置類

  • SpringSecurityConfig.java
package com.sunxiaping.springsecurity.config;

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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * SpringSecurity的配置類
 *
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:38
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("123456");
        auth.inMemoryAuthentication().withUser("admin").password(password).roles("ADMIN");
    }
}

1.3 自定義編寫UserDetailsService實現類

  • UserDetailsServiceImpl.java
package com.sunxiaping.springsecurity.service;

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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:57
 */
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if (!"admin".equals(username)) {
            throw new UsernameNotFoundException("用戶不存在");
        }
        String password = passwordEncoder.encode("123456");
        Collection<? extends GrantedAuthority> authorities = Stream.of(new SimpleGrantedAuthority("ADMIN")).collect(Collectors.toList());
        UserDetails userDetails = new User(username, password, authorities);
        return userDetails;
    }
}
  • SpringSecurityConfig.java
package com.sunxiaping.springsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import javax.naming.Name;

/**
 * SpringSecurity的配置類
 *
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:38
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier(value = "userDetailsService")
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }
}

2 查詢數據庫完成認證

2.1 導入JPA等相關jar包的Maven坐標

  • 修改部分:
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
  • 完整部分:
<?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>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.sunxiaping</groupId>
    <artifactId>spring-security</artifactId>
    <version>1.0</version>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2.2 sql腳本

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

insert into (id,username,password) users values(1,'admin','$2a$10$Ug6eGqhTX7wY8ZVIe2PbC.ljVWEko5h7ZH92N.rg0ZjZBcgg2VOqm');

2.3 修改配置文件

  • application.properties
server.port=8181
# 數據庫連接配置信息
spring.datasource.url=jdbc:mysql://192.168.1.57:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
# JPA相關配置
spring.jpa.show-sql=true
spring.jpa.database=mysql
spring.jpa.hibernate.ddl-auto=update
spring.jpa.open-in-view=true

2.4 編寫實體類

  • Users.java
package com.sunxiaping.springsecurity.domain;

import lombok.*;

import javax.persistence.*;
import java.io.Serializable;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 14:37
 */
@Table(name = "users")
@Entity
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Users implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;

    private String password;
}

2.5 編寫UsersRepository接口

package com.sunxiaping.springsecurity.dao;

import com.sunxiaping.springsecurity.domain.Users;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 14:43
 */
public interface UsersRepository extends JpaRepository<Users, Long>, JpaSpecificationExecutor<Users> {

}

2.6 編寫UserDetailsService的實現類

package com.sunxiaping.springsecurity.service;

import com.sunxiaping.springsecurity.dao.UsersRepository;
import com.sunxiaping.springsecurity.domain.Users;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:57
 */
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UsersRepository usersRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        Optional<Users> optional = usersRepository.findOne(Example.of(Users.builder().username(username).build()));

        Users users = optional.orElseThrow(() -> new UsernameNotFoundException("用戶不存在"));

        Collection<? extends GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("ADMIN");

        UserDetails userDetails = new User(username, users.getPassword(), authorities);
        return userDetails;
    }
}

2.7 編寫SpringSecurity的配置類

package com.sunxiaping.springsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * SpringSecurity的配置類
 *
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:38
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier(value = "userDetailsService")
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }
}

2.8 啟動類

package com.sunxiaping.springsecurity;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EnableJpaAuditing
@EnableTransactionManagement
public class SpringSecurityApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringSecurityApplication.class, args);
    }

}

3 未認證請求跳轉到登錄頁面

3.1 導入thymeleaf相關jar包的Maven坐標

  • 修改部分:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
  • 完整部分:
<?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>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.sunxiaping</groupId>
    <artifactId>spring-security</artifactId>
    <version>1.0</version>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3.2 編寫WebMvcConfig

package com.sunxiaping.springsecurity.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 16:07
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login.html").setViewName("login");
        registry.addViewController("/").setViewName("login");
//        registry.addViewController("/login").setViewName("login");
//        registry.addViewController("/index").setViewName("login");
        registry.addViewController("/success").setViewName("success");
        registry.addViewController("/failure").setViewName("failure");
    }
}

3.3 在templates目錄下新建login.html、success.html和failure.html

在templates目錄下新建login.html、success.html和failure.html

  • login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登錄頁面</title>
</head>
<body>

<form th:action="@{/login}" method="post">
    用戶名: <input type="text" name="username"><br>
    密碼:<input type="password" name="password"> <br>
    <input type="submit" value="登錄">
</form>

</body>
</html>
  • success.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    登錄成功
</body>
</html>
  • failure.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    登錄失敗
</body>
</html>

3.4 修改SpringSecurityConfig

package com.sunxiaping.springsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * SpringSecurity的配置類
 *
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:38
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier(value = "userDetailsService")
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin() //自定義自己編寫的登錄頁面
                .loginPage("/login.html") //登錄頁面設置
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/login")//登錄訪問的路徑
                .defaultSuccessUrl("/success") //登錄成功,跳轉的路徑
                .failureUrl("/failure")
                .permitAll() //指定路徑,無需保護
                .and()
                .authorizeRequests()
                .antMatchers("/", "/login", "/test/hello")
                .permitAll()
                .anyRequest() //其他路徑,需要認證
                .authenticated()
                .and().csrf().disable();//關閉CSRF
    }
}

4 基於角色或權限進行訪問控制

4.1 hasAuthority()方法

  • 如果當前的主體具有指定的權限,則返回true,否則返回false。

  • 修改SpringSecurityConfig.java

package com.sunxiaping.springsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * SpringSecurity的配置類
 *
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:38
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier(value = "userDetailsService")
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin() //自定義自己編寫的登錄頁面
                .loginPage("/login.html") //登錄頁面設置
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/login")//登錄訪問的路徑
                .defaultSuccessUrl("/success") //登錄成功,跳轉的路徑
                .failureUrl("/failure")
                .permitAll() //指定路徑,無需保護
                .and()
                .authorizeRequests()
                .antMatchers("/", "/login", "/test/hello").permitAll()
                //-----------設置權限admins------------
                .antMatchers("/test/hello").hasAuthority("admins")
                .anyRequest() //其他路徑,需要認證
                .authenticated()
                .and().csrf().disable();//關閉CSRF
    }
}
  • 修改UserDetailsServiceImpl.java
package com.sunxiaping.springsecurity.service;

import com.sunxiaping.springsecurity.dao.UsersRepository;
import com.sunxiaping.springsecurity.domain.Users;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.Service;

import java.util.Collection;
import java.util.Optional;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:57
 */
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UsersRepository usersRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        Optional<Users> optional = usersRepository.findOne(Example.of(Users.builder().username(username).build()));

        Users users = optional.orElseThrow(() -> new UsernameNotFoundException("用戶不存在"));

        //這邊的admins要和SpringSecurityConfig中.antMatchers("/test/hello").hasAuthority("admins")中的admins一致
        Collection<? extends GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("admins");

        UserDetails userDetails = new User(username, users.getPassword(), authorities);
        return userDetails;
    }
}

4.2 hasAnyAuthority方法

  • 如果當前的主體具有很多指定的權限的話,返回true。

  • 修改SpringSecurityConfig.java

package com.sunxiaping.springsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * SpringSecurity的配置類
 *
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:38
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier(value = "userDetailsService")
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin() //自定義自己編寫的登錄頁面
                .loginPage("/login.html") //登錄頁面設置
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/login")//登錄訪問的路徑
                .defaultSuccessUrl("/success") //登錄成功,跳轉的路徑
                .failureUrl("/failure")
                .permitAll() //指定路徑,無需保護
                .and()
                .authorizeRequests()
                .antMatchers("/", "/login", "/test/hello").permitAll()
                //-----------設置權限admins,hellos------------
                .antMatchers("/test/hello").hasAnyAuthority("admins", "hellos")
                .anyRequest() //其他路徑,需要認證
                .authenticated()
                .and().csrf().disable();//關閉CSRF
    }
}
  • 修改UserDetailsServiceImpl.java
package com.sunxiaping.springsecurity.service;

import com.sunxiaping.springsecurity.dao.UsersRepository;
import com.sunxiaping.springsecurity.domain.Users;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.Service;

import java.util.Collection;
import java.util.Optional;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:57
 */
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UsersRepository usersRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        Optional<Users> optional = usersRepository.findOne(Example.of(Users.builder().username(username).build()));

        Users users = optional.orElseThrow(() -> new UsernameNotFoundException("用戶不存在"));

        //這邊的admin和hellos要和SpringSecurityConfig中.antMatchers("/test/hello").hasAnyAuthority("admins", "hellos")中的admins和hellos一致
        Collection<? extends GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("admins,hellos");

        UserDetails userDetails = new User(username, users.getPassword(), authorities);
        return userDetails;
    }
}

4.3 hasRole方法

  • 如果用戶具備給定角色就允許訪問,否則出現403。

  • 修改SpringSecurityConfig.java

package com.sunxiaping.springsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * SpringSecurity的配置類
 *
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:38
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier(value = "userDetailsService")
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin() //自定義自己編寫的登錄頁面
                .loginPage("/login.html") //登錄頁面設置
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/login")//登錄訪問的路徑
                .defaultSuccessUrl("/success") //登錄成功,跳轉的路徑
                .failureUrl("/failure")
                .permitAll() //指定路徑,無需保護
                .and()
                .authorizeRequests()
                .antMatchers("/", "/login", "/test/hello").permitAll()
                //-----------設置角色為ADMIN,那么數據庫中對應的角色必須為ROLE_ADMIN------------
                .antMatchers("/test/hello").hasRole("ADMIN")
                .anyRequest() //其他路徑,需要認證
                .authenticated()
                .and().csrf().disable();//關閉CSRF
    }
}
  • 修改UserDetailsServiceImpl.java
package com.sunxiaping.springsecurity.service;

import com.sunxiaping.springsecurity.dao.UsersRepository;
import com.sunxiaping.springsecurity.domain.Users;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.Service;

import java.util.Collection;
import java.util.Optional;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:57
 */
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UsersRepository usersRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        Optional<Users> optional = usersRepository.findOne(Example.of(Users.builder().username(username).build()));

        Users users = optional.orElseThrow(() -> new UsernameNotFoundException("用戶不存在"));

        Collection<? extends GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN");

        UserDetails userDetails = new User(username, users.getPassword(), authorities);
        return userDetails;
    }
}

4.4 hasAnyRole方法

  • 表示用戶具備任何一個角色都可以訪問。

  • 修改SpringSecurityConfig.java

package com.sunxiaping.springsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * SpringSecurity的配置類
 *
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:38
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier(value = "userDetailsService")
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin() //自定義自己編寫的登錄頁面
                .loginPage("/login.html") //登錄頁面設置
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/login")//登錄訪問的路徑
                .defaultSuccessUrl("/success") //登錄成功,跳轉的路徑
                .failureUrl("/failure")
                .permitAll() //指定路徑,無需保護
                .and()
                .authorizeRequests()
                .antMatchers("/", "/login", "/test/hello").permitAll()
                //-----------設置角色為ADMIN,那么數據庫中對應的角色必須為ROLE_ADMIN------------
                .antMatchers("/test/hello").hasAnyRole("ADMIN", "TEST")
                .anyRequest() //其他路徑,需要認證
                .authenticated()
                .and().csrf().disable();//關閉CSRF
    }
}
  • 修改UserDetailsServiceImpl.java
package com.sunxiaping.springsecurity.service;

import com.sunxiaping.springsecurity.dao.UsersRepository;
import com.sunxiaping.springsecurity.domain.Users;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.Service;

import java.util.Collection;
import java.util.Optional;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:57
 */
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UsersRepository usersRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        Optional<Users> optional = usersRepository.findOne(Example.of(Users.builder().username(username).build()));

        Users users = optional.orElseThrow(() -> new UsernameNotFoundException("用戶不存在"));

        Collection<? extends GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN,ROLE_TEST");

        UserDetails userDetails = new User(username, users.getPassword(), authorities);
        return userDetails;
    }
}

5 自定義403頁面

  • 修改訪問配置類:
package com.sunxiaping.springsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * SpringSecurity的配置類
 *
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:38
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier(value = "userDetailsService")
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //自定義403頁面
        http.exceptionHandling().accessDeniedPage("/403");

        http.formLogin() //自定義自己編寫的登錄頁面
                .loginPage("/login.html") //登錄頁面設置
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/login")//登錄訪問的路徑
                .defaultSuccessUrl("/success") //登錄成功,跳轉的路徑
                .failureUrl("/failure")
                .permitAll() //指定路徑,無需保護
                .and()
                .authorizeRequests()
                .antMatchers("/", "/login", "/test/hello").permitAll()
                //-----------設置角色為ADMIN,那么數據庫中對應的角色必須為ROLE_ADMIN------------
                .antMatchers("/test/hello").hasAnyRole("ADMIN", "TEST")
                .anyRequest() //其他路徑,需要認證
                .authenticated()
                .and().csrf().disable();//關閉CSRF
    }
}
  • 在templates目錄下新建error目錄,並新建4xx.html文件:

在templates目錄下新建error目錄,並新建4xx.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>403</title>
</head>
<body>
    對不起,你沒有權限訪問。
</body>
</html>

6 使用注解進行用戶授權

6.1 前提

  • 在啟動類或配置類中開啟全局方法級別的安全控制:
package com.sunxiaping.springsecurity;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EnableJpaAuditing
@EnableTransactionManagement
//開啟全局方法級別的安全控制
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SpringSecurityApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringSecurityApplication.class, args);
    }

}

6.2 @Secured注解

  • 此注解用於判斷是否具有某個角色,需要注意的是,匹配的字符串前面需要添加“ROLE_”。

  • 示例:

package com.sunxiaping.springsecurity.controller;

import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 16:16
 */
@RestController
public class TestController {

    @Secured({"ROLE_SALE","ROLE_ADMIN"})
    @GetMapping(value = "/test/hello")
    public String hello(){
        return "test/hello";
    }

}
package com.sunxiaping.springsecurity.service;

import com.sunxiaping.springsecurity.dao.UsersRepository;
import com.sunxiaping.springsecurity.domain.Users;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.Service;

import java.util.Collection;
import java.util.Optional;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:57
 */
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UsersRepository usersRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        Optional<Users> optional = usersRepository.findOne(Example.of(Users.builder().username(username).build()));

        Users users = optional.orElseThrow(() -> new UsernameNotFoundException("用戶不存在"));

        //AuthorityUtils的commaSeparatedStringToAuthorityList方法的入參需要和@Secured中的一致,否則會報錯,當然實際項目中是從數據庫中查詢得到的
        Collection<? extends GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_SALE,ROLE_ADMIN");

        UserDetails userDetails = new User(username, users.getPassword(), authorities);
        return userDetails;
    }
}

6.2 @PreAuthorize注解

  • @PreAuthorize注解適合進入方法前的權限驗證。

  • 示例:

package com.sunxiaping.springsecurity.controller;

import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 16:16
 */
@RestController
public class TestController {

    @PreAuthorize("hasAnyAuthority('sys:test','sys:add')")
    @GetMapping(value = "/test/hello")
    public String hello(){
        return "test/hello";
    }

}
package com.sunxiaping.springsecurity.service;

import com.sunxiaping.springsecurity.dao.UsersRepository;
import com.sunxiaping.springsecurity.domain.Users;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.Service;

import java.util.Collection;
import java.util.Optional;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:57
 */
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UsersRepository usersRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        Optional<Users> optional = usersRepository.findOne(Example.of(Users.builder().username(username).build()));

        Users users = optional.orElseThrow(() -> new UsernameNotFoundException("用戶不存在"));

        //AuthorityUtils的commaSeparatedStringToAuthorityList方法的入參需要和@PreAuthorize中的一致,否則會報錯,當然實際項目中是從數據庫中查詢得到的
        Collection<? extends GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("sys:test,sys:add");

        UserDetails userDetails = new User(username, users.getPassword(), authorities);
        return userDetails;
    }
}

6.3 @PostAuthorize注解

  • @PostAuthorize注解使用的不多,在方法執行后再進行權限校驗,適合驗證帶有返回值的權限。

  • 示例:

package com.sunxiaping.springsecurity.controller;

import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 16:16
 */
@RestController
public class TestController {

    @PostAuthorize("hasAnyAuthority('sys:test','sys:add')")
    @GetMapping(value = "/test/hello")
    public String hello(){
        return "test/hello";
    }

}
package com.sunxiaping.springsecurity.service;

import com.sunxiaping.springsecurity.dao.UsersRepository;
import com.sunxiaping.springsecurity.domain.Users;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.Service;

import java.util.Collection;
import java.util.Optional;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:57
 */
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UsersRepository usersRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        Optional<Users> optional = usersRepository.findOne(Example.of(Users.builder().username(username).build()));

        Users users = optional.orElseThrow(() -> new UsernameNotFoundException("用戶不存在"));

        //AuthorityUtils的commaSeparatedStringToAuthorityList方法的入參需要和@PostAuthorize中的一致,否則會報錯,當然實際項目中是從數據庫中查詢得到的
        Collection<? extends GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("sys:test,sys:add");

        UserDetails userDetails = new User(username, users.getPassword(), authorities);
        return userDetails;
    }
}

7 用戶注銷

7.1 在配置類中添加退出的映射地址

  • SpringSecurityConfig的修改部分:
//添加退出的映射地址
http.logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll();
  • SpringSecurityConfig的完整部分:
package com.sunxiaping.springsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * SpringSecurity的配置類
 *
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:38
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier(value = "userDetailsService")
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置沒有權限訪問跳轉自定義頁面
        http.exceptionHandling().accessDeniedPage("/403.html");

        //添加退出的映射地址
        http.logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll();

        http.formLogin() //自定義自己編寫的登錄頁面
                .loginPage("/login.html") //登錄頁面設置
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/login")//登錄訪問的路徑
                .defaultSuccessUrl("/success") //登錄成功,跳轉的路徑
                .failureUrl("/failure")
                .permitAll() //指定路徑,無需保護
                .and()
                .authorizeRequests()
                .antMatchers("/", "/login", "/test/hello").permitAll()
                .anyRequest() //其他路徑,需要認證
                .authenticated()
                .and().csrf().disable();//關閉CSRF
    }
}

8 自動登錄

8.1 自動登錄原理

自動登錄原理

8.2 自動登錄

  • 創建配置類,向容器中注入PersistentTokenRepository組件。
package com.sunxiaping.springsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

/**
 * SpringSecurity的配置類
 *
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:38
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier(value = "userDetailsService")
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    private DataSource dataSource;

    /**
     * 向容器中注入PersistentTokenRepository組件
     *
     * @return
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        //自動創建表,第一次執行會創建,以后執行需要注釋掉
//        jdbcTokenRepository.setCreateTableOnStartup(true);
        return jdbcTokenRepository;
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置沒有權限訪問跳轉自定義頁面
        http.exceptionHandling().accessDeniedPage("/403.html");

        //添加退出的映射地址
        http.logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll();

        http.formLogin() //自定義自己編寫的登錄頁面
                .loginPage("/login.html") //登錄頁面設置
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/login")//登錄訪問的路徑
                .defaultSuccessUrl("/success") //登錄成功,跳轉的路徑
                .failureUrl("/failure")
                .permitAll() //指定路徑,無需保護
                .and()
                .authorizeRequests()
                .antMatchers("/", "/login", "/test/hello").permitAll()
                .anyRequest() //其他路徑,需要認證
                .authenticated()
                .and().csrf().disable();//關閉CSRF
    }
}
  • 在配置類中,開啟記住我功能。
package com.sunxiaping.springsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

/**
 * SpringSecurity的配置類
 *
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-28 13:38
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier(value = "userDetailsService")
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    private DataSource dataSource;

    /**
     * 向容器中注入PersistentTokenRepository組件
     *
     * @return
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        //自動創建表,第一次執行會創建,以后執行需要注釋掉
//        jdbcTokenRepository.setCreateTableOnStartup(true);
        return jdbcTokenRepository;
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置沒有權限訪問跳轉自定義頁面
        http.exceptionHandling().accessDeniedPage("/403.html");

        //添加退出的映射地址
        http.logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll();

        http.formLogin() //自定義自己編寫的登錄頁面
                .loginPage("/login.html") //登錄頁面設置
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/login")//登錄訪問的路徑
                .defaultSuccessUrl("/success") //登錄成功,跳轉的路徑
                .failureUrl("/failure")
                .permitAll() //指定路徑,無需保護
                .and()
                .authorizeRequests()
                .antMatchers("/", "/login", "/test/hello").permitAll()
                .anyRequest() //其他路徑,需要認證
                .authenticated()
                .and().csrf().disable();//關閉CSRF

        //開啟記住我功能,並設置記住我為一周
        http.rememberMe().tokenRepository(persistentTokenRepository()).userDetailsService(userDetailsService).tokenValiditySeconds(7 * 24 * 60 * 60);
    }
}
  • 在頁面添加記住我復選框:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登錄頁面</title>
</head>
<body>

<form th:action="@{/login}" method="post">
    用戶名: <input type="text" name="username"><br>
    密碼:<input type="password" name="password"> <br>
    記住我:<input type="checkbox" name="remember-me" id="">
    <input type="submit" value="登錄">
</form>

</body>
</html>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM