SpringBoot框架:集成Security完成认证鉴权
一、导入依赖包
1、导入security相关包:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
二、基础部署
1、HelloController.java:
在Controller层里编写一些简单的接口,用于测试认证和鉴权。
package com.example.demo.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @RequestMapping("/hello") public String hello(){ return "hello"; } @RequestMapping("/404") public String hello404(){ return "404"; } @RequestMapping("/403") public String hello403(){ return "403"; } @RequestMapping("/500") public String hello500(){ return "500"; } }
2、UserPo.java:
用户实体类,用来存储用户信息和用户所拥有的的角色权限。
package com.example.demo.po; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class UserPo implements Serializable, UserDetails { /** 主键id */ private Long id; /** 用户名 */ private String userName; /** 密码 */ private String password; /** 昵称 */ private String nickName; /** 用户是否可用 */ private boolean enabled; /** 角色集合 */ private List<RolePo> roles; /** 注册时间 */ private String regTime; public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public String getUsername() { return userName; } /** * 获取角色权限 * @return */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { List<GrantedAuthority> authorities = new ArrayList<>(); for (RolePo role : roles) { authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName())); } return authorities; } /** * 账户未使用 * @return */ @Override public boolean isAccountNonExpired() { return true; } /** * 账户未锁定 * @return */ @Override public boolean isAccountNonLocked() { return true; } /** * 权限未启用 * @return */ @Override public boolean isCredentialsNonExpired() { return true; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } @Override public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public List<RolePo> getRoles() { return roles; } public void setRoles(List<RolePo> roles) { this.roles = roles; } public String getRegTime() { return regTime; } public void setRegTime(String regTime) { this.regTime = regTime; } }
该类实现UserDetails接口,这个是Sercurity里自带的类,而且实现其中的各个方法,像getAuthorities这个方法是比较重要的,要将自己从数据库里读出的用户角色进行处理,带上角色前缀“ROLE_”,用于鉴权的hasRole方法使用。
类似于isAccountNonExpired、isAccountNonLocked、isCredentialsNonExpired这些方法,要返回true,否则在登录时会提示用户被锁住等信息。
3、UserService.java:
UserService直接继承UserDetailsService类,在实现类里对该类的方法进行重写。
package com.example.demo.service; import org.springframework.security.core.userdetails.UserDetailsService; public interface UserService extends UserDetailsService { }
4、UserServiceImpl.java:
UserServiceImpl实现UserDetailsService中的loadUserByUsername:
package com.example.demo.service.impl; import com.example.demo.mapper.RoleMapper; import com.example.demo.mapper.UserMapper; import com.example.demo.po.RolePo; import com.example.demo.po.UserPo; import com.example.demo.service.UserService; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import java.util.List; @Service public class UserServiceImpl implements UserService { @Autowired UserMapper userMapper; @Autowired RoleMapper roleMapper; /** * 根据用户名获取用户信息 * @param s * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { //参数为空,返回空 if(StringUtils.isEmpty(s)){ return null; } //参数不为空,返回获取到的用户信息 UserPo userPo = userMapper.getUserByUserName(s); //填充用户角色信息 List<RolePo> rolePos = roleMapper.getRolesByUserId(userPo.getId()); userPo.setRoles(rolePos); userPo.getAuthorities(); return userPo; } }
根据用户名获取用户信息,并通过用户与角色的关联关系取得角色信息,使用getAuthorities方法将角色配给用户。
5、WebSecurityConfig.java:
该类继承WebSecurityConfigurerAdapter适配器,添加加密工具以及配置相关接口的开放和拦截。
package com.example.demo.config; import com.example.demo.service.UserService; 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.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired UserService userService; /** * 获取加密工具 * @return */ @Bean public PasswordEncoder getPasswordEncoder(){ return new BCryptPasswordEncoder(); } @Override public void configure(AuthenticationManagerBuilder auth) throws Exception{ auth.userDetailsService(userService) .passwordEncoder(getPasswordEncoder()); } /** * 处理接口的开放和拦截 * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception{ http.authorizeRequests() //开放拦截 .antMatchers("/500").permitAll() .antMatchers("/403").permitAll() .antMatchers("/404").permitAll() .antMatchers("/hello").hasRole("admin") //其他任何请求都要进行身份认证 .anyRequest() .authenticated() .and() //表单提交数据 .formLogin() //配置登录接口 .loginProcessingUrl("/login") .and() //关闭跨站请求伪造保护 .csrf().disable(); } }
按照上面的配置,现在404、403、500接口都是开放的,认证之后即可访问,不做权限拦截。
但是hello这个接口需要登录认证之后,再进行权限判断,即角色字段要与hasRole中的一致。