spring security 数据库验证

前一篇文章讲解了spring security配置自定义登录页面,而登录所需要的用户名和密码是在配置文件中设置的,而在实际开发中,我们希望的是用户和权限信息在数据库中。那么验证就需要通过数据库,本文继续沿用上一篇的案例,主要讲解通过数据库进行验证。(为了方便测试就不查询数据库啦,我们new一个User~)

pom文件

<?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.3.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.sunyuqi</groupId>
	<artifactId>springboot-sercurity-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>springboot-sercurity-demo</name>
	<description>Demo project for Spring Boot</description>

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

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</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>

前端页面

均放在templates目录下
在这里插入图片描述

登录页面login.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <title>Spring Security Example </title>
</head>
<body>
<div th:if="${param.error}">
    用户名或密码错误
</div>
<form action="" method="post">
    <div><label> 账号 : <input type="text" name="username"/> </label></div>
    <div><label> 密码 : <input type="password" name="password"/> </label></div>
    <div><input type="submit" value="登录"/></div>
</form>
</body>
</html>

index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>你好 <span th:text="${username}"></span></h3>
<button type="button" onclick="window.location='/dologout'">登出</button>
</body>
</html>

controller

package com.sunyuqi.controller;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller
public class UserController {

    @RequestMapping("/user/index")
    public String index(Authentication authentication,Model model){
        model.addAttribute("username",authentication.getName());
        return "index";
    }

    /**
     * 登录页面
     * @return
     */
    @RequestMapping("/login")
    public String login(){
        return "login";
    }

    /**
     * 登出
     * @param request
     * @param response
     * @param model
     * @return
     */
    @RequestMapping({"/dologout"})
    public String logout(HttpServletRequest request, HttpServletResponse response,Model model) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null) {//清除认证
            new SecurityContextLogoutHandler().logout(request, response, auth);
        }
        return "redirect:/login";
    }
}

User实体类

package com.sunyuqi.pojo;

import java.util.List;

public class User {
    private Long userId;
    private String username;
    private String sex;
    private String password;
    private List<String> roles;

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public List<String> getRoles() {
        return roles;
    }

    public void setRoles(List<String> roles) {
        this.roles = roles;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", username='" + username + '\'' +
                ", sex='" + sex + '\'' +
                ", password='" + password + '\'' +
                ", roles=" + roles +
                '}';
    }
}

security config

该配置了自定义登录页面,自定义登录校验等…
我们还设置了/user这个路径下的页面需要user权限

package com.sunyuqi.config;

import com.sunyuqi.service.MyUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailService userDetailService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.authorizeRequests()
            // 设置权限
            .antMatchers("/user/**").hasAnyRole("admin", "user")
            .anyRequest().authenticated()
            .and().httpBasic().and()
            // 默认登录页面
            .formLogin().loginPage("/login")
            // 默认登录成功页面
            .defaultSuccessUrl("/user/index")
            // 登录失败跳转
            .failureForwardUrl("/login?error")
            .permitAll()
            .and().logout()
            .logoutUrl("/dologout")
            .logoutSuccessUrl("/login");
    }

    /**
     * 添加 UserDetailsService, 实现自定义登录校验
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            // 从数据库读取的用户进行身份认证
            .userDetailsService(userDetailService)
            .passwordEncoder(passwordEncoder());
    }

    /**
     * 密码加密
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        // 使用BCrypt加密密码
        return new BCryptPasswordEncoder();
    }
}

MyUserDetailService

自定义登录校验需要我们实现UserDetailService接口
该类中我们设置的登录用户名为zhangsan,密码为qwe123,权限为user(实际开发中是要查询数据库的)

package com.sunyuqi.service;

import com.sunyuqi.pojo.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.Component;

import java.util.ArrayList;

@Component
public class MyUserDetailService implements UserDetailsService {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyUserDetailService.class);
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        LOGGER.info("对用户 [{}] 进行信息加载...",username);
        /* 从数据库里面查出用户 */
        User user = new User();
        user.setUserId(1L);
        user.setUsername("zhangsan");
        user.setPassword(passwordEncoder.encode("qwe123"));
        user.setSex("男");
        ArrayList<String> roles = new ArrayList<>();
        roles.add("ROLE_user");
        user.setRoles(roles);
        if(user==null){
            LOGGER.error("用户 [{}] 未找到",username);
            throw new UsernameNotFoundException("Username:["+username+"] not found");
        }
        LOGGER.info("用户 [{}] 信息加载完成",username);
        // SecurityUser 实现UserDetails
        return new SecurityUser(user);
    }
}

loadUserByUsername需要返回一个 UserDetails接口,我们新建一个类实现该接口

SecurityUser

package com.sunyuqi.service;

import com.sunyuqi.pojo.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
import java.util.List;

public class SecurityUser extends User implements UserDetails {
    private static final Logger log = LoggerFactory.getLogger(SecurityUser.class);
    SecurityUser(User user){
        if (user != null){
            this.setUserId(user.getUserId());
            this.setUsername(user.getUsername());
            this.setSex(user.getSex());
            this.setPassword(user.getPassword());
            this.setRoles(user.getRoles());
        }
    }

    /**
     * 权限集合
     * @return
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        List<String> roles = this.getRoles();
        if(roles != null){
            for (String role : roles) {
                SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role);
                authorities.add(authority);
            }
        }
        log.info("获取登录用户已具有的权限:{}", authorities.toString());
        return authorities;
    }

    /**
     * 指示用户的账户是否已过期。无法验证过期的账户。
     * @return 如果用户的账户有效(即未过期),则返回true,如果不在有效就返回false
     */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    /**
     * 指示用户是锁定还是解锁。无法对锁定的用户进行身份验证。
     * @return 如果用户未被锁定,则返回true,否则返回false
     */
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /**
     * 指示用户的凭证(密码)是否已过期。过期的凭证阻止身份验证
     * @return 如果用户的凭证有效(即未过期),则返回true
     *         如果不在有效(即过期),则返回false
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /**
     * 指示用户是启用还是禁用。无法对禁用的用户进行身份验证
     * @return 如果启用了用户,则返回true,否则返回false
     */
    @Override
    public boolean isEnabled() {
        return true;
    }
}

引导类

package com.sunyuqi;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
public class SpringbootSercurityDemoApplication {

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

运行项目,访问http://localhost:8080/login
在这里插入图片描述
输入我们设置的用户名和密码,登录成功,同时因为该用户赋予了user权限,可以访问/user路径下的index页面
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值