Spring Security提供了默认数据库模板和自定义数据库两种方式,默认数据库方式就不说了,因为基本没人用到
建立下相关controller,为不同权限做准备
@RestController
@RequestMapping("admin/api")
public class AdminController {
/**
* 管理员访问路径
* @return
*/
@GetMapping("hello")
public String hello(){
return "admin,hello";
}
}
@RestController
@RequestMapping("/user/api")
public class UserController {
/**
* 注册用户访问路径
* @return
*/
@GetMapping("hello")
public String hello(){
return "user,hello";
}
}
@RestController
@RequestMapping("/app/api")
public class AppController {
/**
* 游客访问路径
* @return
*/
@GetMapping("hello")
public String hello(){
return "app,hello";
}
}
顺便演示一下内存多用户配置
2.2.1 内存多用户配置
配置访问路径权限和用户组权限
package com.example.configuration;
import org.springframework.context.annotation.Bean;
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.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/api/**").hasRole("ADMIN")
.antMatchers("/user/api/**").hasRole("USER")
.antMatchers("/app/api/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin();
}
@Bean
public UserDetailsService userDetailsService(){
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("{noop}123").roles("USER").build());
manager.createUser(User.withUsername("admin").password("{noop}123").roles("USER,ADMIN").build());
return manager;
}
}
在这里使用InMemoryUserDetailsManager 生成了两个用户,将用户寄存在内存中
直接登录http:localhost:8080/user/api/hello,会跳转到登录页面
使用user用户登录
跳转到admin/api/hello
显示403错误,即认证成功但是权限不够
2.2.2 数据库多用户
Security提供了UserDetailsService实现类,可以利用他实现自定义的数据库查找用户,使用loadUserByUsername来获取用户
因此这里工程需要接入MyBatis
添加相关的配置并建立mysql数据库
CREATE TABLE USERS(
ID BIGINT(20) PRIMARY KEY NOT NULL AUTO_INCREMENT,
USERNAME VARCHAR(50) NOT NULL,
PASSWORD VARCHAR(60),
ENABLE TINYINT(4) NOT NULL DEFAULT '1' COMMENT '用户是否可用',
ROLES TEXT CHARACTER SET UTF8 COMMENT '用户角色'
);
INSERT INTO USERS(USERNAME,PASSWORD,ENABLE,ROLES) VALUES("admin","123456","1","ROLE_ADMIN,ROLE_USER");
INSERT INTO USERS(USERNAME,PASSWORD,ENABLE,ROLES) VALUES("user","123456","1","ROLE_USER");
其中,USERS表存用户、密码、是否可用和用户角色(多个角色用逗号分隔)
编写相关的User实体类
实体类需实现UserDetails,UserDetails对象包含了一系列在验证时会用到的信息,包括用户名、密码、权限以及其他信息,SpringSecurity会根据这些信息判断验证是否成功
package com.example.pojo;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
public class User implements UserDetails {
private Long id;
private String username;
private String password;
private String roles;
private boolean enable;
private List<GrantedAuthority> authorities;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public String getRoles() {
return roles;
}
public void setRoles(String roles) {
this.roles = roles;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
public void setAuthorities(List<GrantedAuthority> authorities) {
this.authorities = authorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorities;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return this.enable;
}
}
POJO实现了UserDetails,其中的一些方法,比如:isAccountNonLocked、isAccountNonLocked和isCredentialsNonExpired这里暂时没用到,统一返回为true,否则会被SpringSecurity认为是账号异常
UserDetailService的实现
UserDetailService定义了一个loadUserByUsername方法,用于获取UserDetails对象
package com.example.service;
import com.example.mapper.UserMapper;
import com.example.pojo.User;
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.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class MyUserDetailService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//从数据库读取
User user = userMapper.findByUserName(username);
if(null == user){
throw new UsernameNotFoundException("用户不存在");
}
//将roles解析为UserDetails的权限集
user.setAuthorities(generateAuthorities(user.getRoles()));
return user;
}
private List<GrantedAuthority> generateAuthorities(String roles){
List<GrantedAuthority> authorities= new ArrayList<>();
String[] roleArrays = roles.split(",");
if(roles != null && !"".equals(roles)){
for (String role : roleArrays) {
authorities.add(new SimpleGrantedAuthority(role));
}
}
return authorities;
}
}
因为SpringSecurity使用的是5.x.x版本,对应的SpringSecurity对明文密码设置有要求,否则会报空指针异常
PasswordEncoder配置
这里对PasswordEncoder做自定义类,做明文密码匹配
@Service
public class MyPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return s.equals(charSequence.toString());
}
}
Security配置类
在WebSecurityConfig配置类中进行配置添加,使用自定义的UserDetailService和PasswordEncoder如下:
@Autowired
private MyUserDetailService myUserDetailService;
@Autowired
private MyPasswordEncoder myPasswordEncoder;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailService).passwordEncoder(myPasswordEncoder);
}
Mapper配置
这里只用配置一个接口就行了,数据库的连接配置就不放出来了
package com.example.mapper;
import com.example.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Component;
@Component
public interface UserMapper {
@Select("SELECT * FROM USERS WHERE USERNAME = #{username}")
User findByUserName(@Param("username") String username);
}
输入http://localhost:8080/user/api/hello,会自动跳转到登录界面
输入用户user,密码123456
输入http://localhost:8080/admin/api/hello
显示权限不够,改用admin账户登录;建立使用无痕模式测试,以免浏览器缓存导致奇奇怪怪的问题
再登入http://localhost:8080/user/api/hello
admin和user路径都可以访问
以上就是数据库实现Spring Security的自定义访问