Spring Security中的用户注销

Posted 爱上口袋的天空

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Security中的用户注销相关的知识,希望对你有一定的参考价值。

实现过程:
登录成功后进入一个页面,点击退出后,无法访问需要权限的页面。

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">
    <parent>
        <artifactId>kgf-java-learning</artifactId>
        <groupId>com.kgf.learning</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../kgf-java-learning/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.kgf.security</groupId>
    <artifactId>spring-security-demo</artifactId>
    <properties>
        <lombok.version>1.18.16</lombok.version>
        <fastjson.version>1.2.75</fastjson.version>
        <druid.version>1.2.2</druid.version>
        <mybatis-plus.version>3.4.1</mybatis-plus.version>
        <mysql.version>5.1.49</mysql.version>
    </properties>
    <dependencies>
        <!--引入mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <!--引入mybatis-plus依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <!-- 引入druid连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--lombok 用来简化实体类-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.2.11.RELEASE</version>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

application.yaml

server:
  port: 8081

#spring:
#  security:
#    user:
#      name: admin
#      password: admin

spring:
  datasource:
    url: jdbc:mysql://192.168.1.13:3306/oss?userUnicode=true&characterEncoding=UTF-8&serverTimeZone=UTC
    username: root
    password: 897570
    driver-class-name: com.mysql.jdbc.Driver

mybatis-plus: #注意:延迟加载不能在开启debug后,不能点开list信息,甚至不能把鼠标移到上面。否则在debug下会默认执行关联查询。
  mapper-locations: classpath:/mappers/**/*.xml
  type-aliases-package: com.kgf.security

login.html

<!DOCTYPE html>
<!-- 需要添加
<html  xmlns:th="http://www.thymeleaf.org">
这样在后面的th标签就不会报错
 -->
<html  xmlns:th="http://www.thymeleaf.org">
<head lang="en">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>xx</title>
</head>
<body>


<h1>表单提交</h1>
<!-- 表单提交用户信息,注意字段的设置,直接是*{} -->
<form action="/user/login"  method="post">
    <input type="text" name="username" />
    <input type="text" name="password" />
    <input type="submit" />
</form>
</body>
</html>

success.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <a href="/logout">退出</a>
</body>
</html>

unauth.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>没有访问权限</h1>
</body>
</html>
SecurityApplication.java
package com.kgf.security;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;

@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
@MapperScan(value = "com.kgf.security.mapper")
@SpringBootApplication
public class SecurityApplication {

    public static void main(String[] args) {

        SpringApplication.run(SecurityApplication.class,args);

    }

}

Users.java

package com.kgf.security.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Users {

    private Integer id;

    private String username;

    private String password;

}
UsersMapper.java
package com.kgf.security.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.kgf.security.model.Users;

public interface UsersMapper extends BaseMapper<Users> {
}
MyUserDetailsService.java
package com.kgf.security.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kgf.security.mapper.UsersMapper;
import com.kgf.security.model.Users;
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.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/***
 * spring security查询用户的时候会自动到这个类中去查找
 */
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {

    @Resource
    private UsersMapper usersMapper;

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

        QueryWrapper<Users> usersQueryWrapper = new QueryWrapper<>();
        usersQueryWrapper.eq("username",username);
        Users users = usersMapper.selectOne(usersQueryWrapper);
        if (users==null){
            throw new UsernameNotFoundException("用户名不存在!");
        }
        //这里我们就不去查询数据库了,直接new一个对象
        List<GrantedAuthority> authorityList = AuthorityUtils.commaSeparatedStringToAuthorityList("manager");
        return new User(username,users.getPassword(),authorityList);
    }
}

SecurityConfig.java

 

完整代码:

package com.kgf.security.config;

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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;


    /***
     * 设置登录的用户
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //这是使用内存的方式
//        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
//        String password = encoder.encode("123456");
//        auth.inMemoryAuthentication().withUser("test").password(password).roles("admin");
        //下面使用自定义接口实现类从数据库查询
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.formLogin()
                .loginPage("/login.html")//登录页面设置
                .loginProcessingUrl("/user/login")//登录访问路径,这个不需要自己写controller,配置即可,这个是spring security自动完成
                .defaultSuccessUrl("/success.html").permitAll()//登录成功之后跳转的路径
                .and().authorizeRequests()
                .antMatchers("/","/test/hello","/user/login").permitAll()//设置这些路径可以直接访问,不需要认证
                //1、hasAuthority方法
                //.antMatchers("/test/index").hasAuthority("admin")//需要用户具有admin角色权限才行
                //2、hasAnyAuthority方法
                .antMatchers("/test/index").hasAnyAuthority("admin,manager")//需要用户具有admin角色权限才行
                .anyRequest().authenticated()
                .and().exceptionHandling().accessDeniedPage("/unauth.html")//配置没有权限访问跳转自定义页面,必须配置在这里,配置在上面不起作用
                .and().logout().logoutUrl("/logout").logoutSuccessUrl("/test/hello").permitAll()//退出
                .and().csrf().disable();//关闭csrf防护
    }

    /***
     * 注入PasswordEncoder到spring中
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

}

 

TestController.java

package com.kgf.security.controller;

import com.kgf.security.model.Users;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RequestMapping("test")
@RestController
public class TestController {


    @RequestMapping("hello")
    public String test(){
        return "hello security";
    }

    @RequestMapping("index")
    public String index(){
        return "hello index";
    }
    @RequestMapping("unauth")
    public String unauth(){
        return "unauth";
    }

    @GetMapping("update")
//    @Secured({"ROLE_sale","ROLE_manager"})
    @PreAuthorize("hasAnyAuthority('manager')")
    public String update() {
        return "hello update";
    }

    @GetMapping("getAll")
    @PreAuthorize("hasAnyAuthority('manager')")
    @PostFilter("filterObject.username == '44'")
    public List<Users> getAllUser() {
        ArrayList<Users> list = new ArrayList<>();
        list.add(new Users(1,"22","33"));
        list.add(new Users(2,"44","33"));
        return list;
    }

}

测试:

 

退出: 

 点击退出之后访问/test/index,重新跳转到登录页面

 

以上是关于Spring Security中的用户注销的主要内容,如果未能解决你的问题,请参考以下文章

通过管理员 Spring Security 注销用户

Spring Security OAuth2 AngularJS |注销流程

如何从 Spring Security 中的 java 代码登录用户?

java spring boot / spring security(HttpSecurity)中的会话到期时如何自动注销

Spring Security中的注销不起作用

您如何在 spring-security 中注销所有已登录的用户?