基于 Spring Boot 的 SSM 环境整合十五:整合 spring security 二(动态获取用户和角色)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xz2001/article/details/86358704

 spring security 是基于【用户】-【角色】-【权限】机制,也就是说权限并不是直接分配给用户,而是分配给角色,再将相应的用户授权角色即可。

1、实现用户信息接口 UserDetails

UserDetails 接口定义了用户的基本信息,如用户名称、密码、账号是否过期、是否有效,以及所拥有的角色等:

package com.whowii.website4.security;

import java.io.Serializable;
import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;

class UserInfo implements Serializable, UserDetails {
	private static final long serialVersionUID = 1L;
	private String username;
	private String password;
	private String role; // 拥有的角色列表(多个角色之间用逗号“,”分隔)
	private boolean accountNonExpired; // 账号是否未过期
	private boolean accountNonLocked; // 账号是否未锁定
	private boolean credentialsNonExpired; // 账号凭证是否未过期
	private boolean enabled; // 账号是否可用

	public UserInfo(String username, String password, String role, boolean accountNonExpired, boolean accountNonLocked,
			boolean credentialsNonExpired, boolean enabled) {
		this.username = username;
		this.password = password;
		this.role = role;
		this.accountNonExpired = true;
		this.accountNonLocked = true;
		this.credentialsNonExpired = true;
		this.enabled = true;
	}

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		return AuthorityUtils.commaSeparatedStringToAuthorityList(role);
	}

	@Override
	public String getPassword() {
		return password;
	}

	@Override
	public String getUsername() {
		return username;
	}

	@Override
	public boolean isAccountNonExpired() {
		return accountNonExpired;
	}

	@Override
	public boolean isAccountNonLocked() {
		return accountNonLocked;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		return credentialsNonExpired;
	}

	@Override
	public boolean isEnabled() {
		return enabled;
	}
}

2、实现获取用户信息的接口 UserDetailsService

package com.whowii.website4.security;

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.Component;

@Component
public class MyUserDetailsService implements UserDetailsService {
	//	@Autowired
	//	private UserService userService;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		System.out.println("MyUserDetailsService.loadUserByUsername > readUser");

		/*
		 理论上,我们使用 UserService 获取用户信息,判断账号是否过期、是否有效,以及角色信息等,这是为了描述方便,我使用变量代替:
		 1、分配 admin 用户,密码为 123,角色为 ROLE_ADMIN(必须在ROLE_开头,这是 spring security强制的)
		 2、分配 user 用户,密码为123,角色为 ROLE_USER
		 3、两个用户账号都未过期、未锁定、账号凭证未过期、账号可用
		 4、其他其他用户名登录时,返回 null 表示用户不存在
		 */
		
		if ("admin".equals(username)) {
			return new UserInfo("admin", "123", "ROLE_ADMIN", true, true, true, true);
		} else if ("user".equals(username)) {
			return new UserInfo("user", "123", "ROLE_USER", true, true, true, true);
		} else {
			return null;
		}
	}
}

正如我在示例代码中所说,这里写成固定的变量只是为了方便演示,实际上可以为类注入 UserService(你自己定义的用户表服务层),以获取用户信息和角色信息。

3、实现用户信息验证器接口 AuthenticationProvider

package com.whowii.website4.security;

import java.util.Collection;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;

@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
	// 注入用户信息获取对象
	@Autowired
	private UserDetailsService userDetailService;

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		System.out.println("MyAuthenticationProvider.authenticate > :");

		String userName = authentication.getName();// 这个获取表单输入中返回的用户名;
		String password = (String) authentication.getCredentials();// 这个是表单中输入的密码;

		UserInfo userInfo = (UserInfo) userDetailService.loadUserByUsername(userName); // 使用登录用户名获取用户信息
		if (userInfo == null) {
			System.out.println("----> 用户名不存在");
			throw new BadCredentialsException("用户名不存在");
		}

		if (!password.equals(userInfo.getPassword())) {
			throw new BadCredentialsException("密码不正确");
		}

		Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
		return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
	}

	@Override
	public boolean supports(Class<?> authentication) {
		return authentication.equals(UsernamePasswordAuthenticationToken.class);
	}
}

4、注册 AuthenticationProvider

在上一篇中我们使用了固定的两个用户,也就是SecurityConfig类中如下代码片段:

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		System.out.println("SecurityConfig.configure > withUser");
		
		auth.inMemoryAuthentication()
			.passwordEncoder(new BCryptPasswordEncoder())
			.withUser("admin").password(new BCryptPasswordEncoder().encode("123")).roles("ADMIN")
			.and()
			.withUser("user").password(new BCryptPasswordEncoder().encode("123")).roles("USER");
	}

这里,需要把这个方法删除,并注入MyAuthenticationProvider,完成后的SecurityConfig类如下:

package com.whowii.website4.security;

import org.springframework.beans.factory.annotation.Autowired;
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;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	@Autowired
	private MyAuthenticationProvider authProvider;

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.authenticationProvider(authProvider);
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		System.out.println("SecurityConfig.configure > http");

		http.csrf().disable().authorizeRequests().antMatchers("/manage/demo/hello").permitAll() // 此页面允许任何人访问,即使未登录
				.antMatchers("/manage/demo/info1").hasAnyRole("ADMIN") // 仅允许 ADMIN 角色的用户访问
				.antMatchers("/manage/demo/info2").hasAnyRole("USER") // 仅允许 USER 角色的用户访问
				.and().formLogin().loginPage("/manage/demo/login") // 自定义登录页面
				.failureUrl("/manage/demo/error") // 登录错误页面
				.permitAll() // 允许任何用户访问
				.and().logout().logoutUrl("/manage/demo/exit") // 退出登录
				.logoutSuccessUrl("/manage/demo/index") // 退出登录成功返回的页面
				.permitAll() // 也允许任务用户访问
				.and().exceptionHandling();
	}

}

5、测试结果

可以参考上一篇说明,测试各个页面,这里不细说。当然,也可以增加一些其他用户和角色。

猜你喜欢

转载自blog.csdn.net/xz2001/article/details/86358704
今日推荐