实现SpringSecurity在数据库中查询用户然后登录认证功能

1、构建工程

参考:前文 步骤一。

2、配置数据库和Security

application.properties内容如下:

#datasource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql:///springsecurity_demo?serverTimezone=UTC&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=root

#jpa
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update

本工程根包为:com.zzz.rss
创建Security配置类:com.zzz.rss.config.SecurityConfig,内容如下:

package com.zzz.rss.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

//web应用安全适配器
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{

	//告诉SpringSecurity 我们密码用什么加密
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		
		//表单登录(身份认证)
		http.formLogin()
			.and()
			//请求授权
			.authorizeRequests()
			//所有请求(包括/user)
			.anyRequest()
			//都需要我们身份认证
			.authenticated();
	}
}

3、三层和实体层的具体实现:

创建Controller层响应类com.zzz.rss.controller.UserController.java,代码如下:

package com.zzz.rss.controller;

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

import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.annotation.JsonView;
import com.zzz.rss.domain.User;

//为Controller提供RestAPI
@RestController
@RequestMapping("/user")  //简化@RequestMapping(value="/user",method=RequestMethod.GET)代码
public class UserController {

	/**
	 * @Title: query   
	 * @Description: 完成查询响应
	 * @param: @return 参数
	 * @return: List<User> 返回类型
	 * @throws
	 */
	//@RequestParam 
	//defaultValue 默认值  
	//name 传入参数的名字 ,对应上方法的参数,如果相同可以省略
	//required 是否必须对应传入参数,默认true,没对应上报400错误
	//value name别名相当于name
	//@RequestMapping(value="/user",method=RequestMethod.GET)用@GetMapping代替,因为类的头部使用了@RequestMapping("/user")
	@GetMapping
	//@RequestParam username获取的是网址http://localhost:8080/user?username=xx的值,不是security的登录认证用户名
	public List<User> query(@RequestParam String username) {
		
		System.out.println(username);
		
		List<User> list = new ArrayList<User>();
		list.add(new User());
		list.add(new User());
		list.add(new User());
		
		return list;
	}
}

创建repository层(相当与dao层)操作数据库接口类com.zzz.rss.repository.UserRepository.java,内容如下:

package com.zzz.rss.repository;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;

import com.zzz.rss.domain.User;

public interface UserRepository extends CrudRepository<User, Long> {

	@Query(value="select * from user where username = ?1",nativeQuery=true)
	User findUserByUsername(String username);
}

创建service层业务类com.zzz.rss.service.UserService.java实现登录认证功能,代码如下:

package com.zzz.rss.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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 com.zzz.rss.domain.User;
import com.zzz.rss.repository.UserRepository;

/**
 * @ClassName:  UserService   
 * @Description: 用SpringSecurity默认的登录系统
 * @author Administrator
 * @date:   2022年3月28日 下午3:39:27      
 * @Copyright:
 */
@Component
public class UserService implements UserDetailsService{

	@Autowired
	private PasswordEncoder passwordEncoder;
	@Autowired
	private UserRepository userRepository;

	//spring security默认处理登录  username为输入的用户名
	//访问任何页面被拦截,跳转login页面需要提供认证
	//输入用户名和密码后,跳转到UserService
	//根据用户名搜索到数据库对应的用户密码
	//拿数据库密码来匹配认证,认证成功则返回domain的User对象表示认证成功,继续访问之前页面
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

		User user = userRepository.findUserByUsername(username);
		
		if (user==null) {
			throw new UsernameNotFoundException(username);
		}
		
		//用户,密码,权限    
		//domain User实现UserDetails接口,拿数据库密码来匹配认证
		return new User(username, passwordEncoder.encode(user.getPassword()), AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
	}
	
}

最后创建domain实体类com.zzz.rss.domain.User.java,因为看起来比较复杂,所以放到最后。其实除了基本的用户属性和get/set方法以外,其他的都是继承Security的UserDetails接口类,参考org.springframework.security.core.userdetails.User实现类写的。
在eclipse菜单栏中找到navigate——》Open Type可搜索Security的User实现类的具体代码:
在这里插入图片描述
自己实现damain层User类的代码如下:

package com.zzz.rss.domain;

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Transient;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;

@Entity
public class User implements UserDetails{
	
	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	private String username;
	private String password;

	@Transient  //不映射到数据库表
	//用户权限
	private Set<GrantedAuthority> authorities;
	@Transient
	//用户是否没失效
	private boolean accountNonExpired;
	@Transient
	//用户是否没冻结
	private boolean accountNonLocked;
	@Transient
	//用户是否没过期
	private boolean credentialsNonExpired;
	@Transient
	//用户是否删除
	private boolean enabled;
	
	//给hibernate用的构造方法
	public User() {
		super();
	}
	
	public User(Long id, String username, String password) {
		super();
		this.id = id;
		this.username = username;
		this.password = password;
	}
	
	//给springsecurity用的构造方法
	public User(String username, String password, Collection<? extends GrantedAuthority> authorities) {
		this(username, password, true, true, true, true, authorities);
	}
	
	public User(String username, String password, boolean enabled, boolean accountNonExpired,
			boolean credentialsNonExpired, boolean accountNonLocked,
			Collection<? extends GrantedAuthority> authorities) {
		Assert.isTrue(username != null && !"".equals(username) && password != null,
				"Cannot pass null or empty values to constructor");
		this.username = username;
		this.password = password;
		this.enabled = enabled;
		this.accountNonExpired = accountNonExpired;
		this.credentialsNonExpired = credentialsNonExpired;
		this.accountNonLocked = accountNonLocked;
		this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
	}
	
	private static SortedSet<GrantedAuthority> sortAuthorities(Collection<? extends GrantedAuthority> authorities) {
		Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection");
		// Ensure array iteration order is predictable (as per
		// UserDetails.getAuthorities() contract and SEC-717)
		SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet<>(new AuthorityComparator());
		for (GrantedAuthority grantedAuthority : authorities) {
			Assert.notNull(grantedAuthority, "GrantedAuthority list cannot contain any null elements");
			sortedAuthorities.add(grantedAuthority);
		}
		return sortedAuthorities;
	}
	
	private static class AuthorityComparator implements Comparator<GrantedAuthority>, Serializable {

		private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

		@Override
		public int compare(GrantedAuthority g1, GrantedAuthority g2) {
			// Neither should ever be null as each entry is checked before adding it to
			// the set. If the authority is null, it is a custom authority and should
			// precede others.
			if (g2.getAuthority() == null) {
				return -1;
			}
			if (g1.getAuthority() == null) {
				return 1;
			}
			return g1.getAuthority().compareTo(g2.getAuthority());
		}

	}

	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	
	//用户权限
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		// TODO Auto-generated method stub
		return authorities;
	}
	//用户是否没失效
	@Override
	public boolean isAccountNonExpired() {
		// TODO Auto-generated method stub
		return accountNonExpired;
	}
	//用户是否没冻结
	@Override
	public boolean isAccountNonLocked() {
		// TODO Auto-generated method stub
		return accountNonLocked;
	}
	//用户是否没过期
	@Override
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return credentialsNonExpired;
	}
	//用户是否删除
	@Override
	public boolean isEnabled() {
		// TODO Auto-generated method stub
		return enabled;
	}
	
}

4、运行测试

在根包com.zzz.rss右键run as——》java application。运行起来后,在浏览器中访问http://localhost:8080/user,需要登录认证,输入用户名和密码即可完成登录。(这里我的数据库已经提前建好,数据库名是springsecurity_demo,user表包含id,password,username三个基本字段,然后自己添加即可测试用户数据即可。)

5、扩展:完成自定义登录页面

创建login.html,一定要放在static下。
在这里插入图片描述

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h1>登录</h1>

	<form action="/loginPage" method="post">
		用户名
		<input type="text" name="username">
		<br>
		密码
		<input type="password" name="password">
		<br>
		<input type="submit">
	</form>
	
</body>
</html>

修改Security配置文件com.zzz.rss.config.SecurityConfig.java:

package com.zzz.rss.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

//web应用安全适配器
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{

	//告诉SpringSecurity 我们密码用什么加密
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		
		//表单登录(身份认证)
		http.formLogin()
			//自定义登录页面:指定html
			.loginPage("/login.html")
			//自定义登录页面:用springsecurity自带的过滤器响应loginPage请求
			.loginProcessingUrl("/loginPage")
			.and()
			//请求授权
			.authorizeRequests()
			//自定义登录页面:允许认证通过
			.antMatchers("/login.html").permitAll()
			//所有请求(包括/user)
			.anyRequest()
			//都需要我们身份认证
			.authenticated()
			//自定义登录页面:跨站请求仿造的防护取消
			.and().csrf().disable();
	}
}

重新运行工程,登录认证就会用自定义界面。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值