SpringSecurity集成数据库
环境配置
- 导入驱动jar包
<!--导入数据库需要的jar包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
- 编写配置文件
spring: thymeleaf: cache: false datasource : username: root password: 1root url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=false&server=Asia/shanghai" driver-class-name: com.mysql.cj.jdbc.Drivermybatis : type-aliases-package : com.linzelin.pojo configuration : map-underscore-to-canel-case : true # 这个是开启驼峰模式
- 创建mapper文件夹
代码编写
用户实体类编写
import com.fasterxml.jackson.annotation.JsonIgnore;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection;import java.util.List;@Data@NoArgsConstructor@AllArgsConstructorpublic class SysUser implements UserDetails { /*这里就是这个用户类的基本字段了*/ private Integer id; private String username; private String password; private Integer status; /*这个就是对应的用户角色的设定了*/ List<SysRole> roles; /*下面的是关于这些基本方法的重写*/ /** * 这里返回的就是我们自定义继承的那个SpringSecurity用户角色的那个类了 * @return */ @JsonIgnore @Override public Collection<? extends GrantedAuthority> getAuthorities() { return this.roles; } /** * 这个就是获取用户对象的一个密码 */ @Override public String getPassword() { return this.password; } /** * 这个就是获取用户对象的一个用户名 */ @Override public String getUsername() { return this.username; } /** * 这里下面的几个返回boolean的方法都是用来判断用户状态的 * @return 返回true代表没问题,返回false有问题 */ @JsonIgnore @Override public boolean isAccountNonExpired() { return true; } @JsonIgnore @Override public boolean isAccountNonLocked() { return true; } @JsonIgnore @Override public boolean isCredentialsNonExpired() { return true; } /** * 这个是代表当前这个用户是否可用 * @return true表示可用,false相反 */ @Override public boolean isEnabled() { return true; }}
-
有没有注意到这里的用户类是继承了UserDetails这个类
-
这个类是SpringSecurity中提供的一个基本用户类
-
其他的就是使用上我们自定义的字段了
- 还有一个要注意的就是这里重写的方法都是返回的我们自定义的字段噢
用户权限类的编写
import com.fasterxml.jackson.annotation.JsonIgnore;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import org.springframework.security.core.GrantedAuthority;/** * 这里实现的是SpringSecurity中内置的一个用户角色 */@Data@NoArgsConstructor@AllArgsConstructorpublic class SysRole implements GrantedAuthority { /*这里依旧是对照我们的角色表来写字段*/ private Integer id; private String roleName; private String context; /*这里加上搞定jsonIgnore是忽略转化为json字段的意思 * 就不会再参与json字符串的使用,因为这个不是我们当前项目的一个属性 * */ @JsonIgnore @Override public String getAuthority() { return this.roleName; }}
-
这个还是老套路,仿造原来的操作即可
-
还是要注意的是这里是继承了SpringSecurity的用户权限类
- 还有我们这里重写的方法返回值是我们自定义的字段的名称
数据库Mapper类编写
- 这里接下来的操作就有点东西了
用户表查询
import com.linzelin.pojo.SysUser;import org.apache.ibatis.annotations.*;import org.springframework.stereotype.Repository;import java.util.List;@Mapper@Repositorypublic interface UserMapper extends tk.mybatis.mapper.common.Mapper<SysUser>{ /*这里因为继承的Mapper和使用的注解的Mapper有冲突,所以这里使用这种方式导入*/ /** * 通过用户名查询对应的字段 */ @Select("select * from user where username=#{username}") @Results({ @Result(id = true,property = "id" ,column = "id"), @Result(property = "roles" ,column = "id" , javaType = List.class ,many = @Many(select = "com.linzelin.mapper.RoleMapper.findByUid")) }) SysUser findByName(String username);}
- 值得注意的是这里我们是需要使用@Mapper这个注解来让給MyBatis注册这个mapper
- 但是下面这里还是需要继承一个mapper,这里的导入的包和注解的包是不一样的
- 所以要用这样一个比较奇怪的方式导入
- 这里是一个使用注解来封装查询结果的方式
- 就是ResultMap这样的操作
- 这里还调用了另外一个类里面的查询
权限表查询
import com.linzelin.pojo.SysRole;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Select;import org.springframework.stereotype.Repository;import java.util.List;@Mapper@Repositorypublic interface RoleMapper extends tk.mybatis.mapper.common.Mapper<SysRole>{ /*这里继承的就是角色的mapper了*/ @Select(" SELECT role.`id`,role.`rolename` AS roleName ,role.`context` " + " FROM role,role_user " + " WHERE role.`id` = role_user.`role_id` AND role_user.`user_id` = #{uid}") List<SysRole> findByUid(Integer uid);}
- 其实我更想使用下mapper.xml来查
- 方便点
UserService类的编写
- 这个对比前面的就简单很多了
import org.springframework.security.core.userdetails.UserDetailsService;public interface UserService extends UserDetailsService {}
- 这里是直接继承 UserDetailsService这个类就可以了
import com.linzelin.mapper.UserMapper;import com.linzelin.service.UserService;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 org.springframework.transaction.annotation.Transactional;@Service@Transactional//这个就是事务的注解public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { return userMapper.findByName(s); //这个类似之前的逻辑,但是这个更加简单粗暴 }}
- Impl还记得吗?
- 之前的Impl是要配置好多东西的
- 现在只要配置返回这个查询的结果就可以了
SecurityConfig配置编写
- 这个就有点小离谱了
import com.linzelin.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.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;/** * @author 君未洋 */@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter { //继承了这个类之后就相当于这个类拥有所有的SpringSecurity所有的配置 @Autowired private UserService userService; /** * 这个就是配置的一个将加密类加入到Spring的容器中的方式 * @return */ @Bean public BCryptPasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } /** * 这个就是认证用户的来源了 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { /* auth.inMemoryAuthentication() .withUser("admin") .password("{noop}123456") .roles("USER"); //注意这里的用户角色不能使用ROLE开头,不然会报错*/ auth.userDetailsService(userService).passwordEncoder(passwordEncoder()); } /** * 配置SpringSecurity基本配置 */ @Override protected void configure(HttpSecurity http) throws Exception { /*这里就是配置基本的登录页面,角色,登录相关的提交等等等*/ http.authorizeRequests() .antMatchers("/login.html","/failer.jsp","/css/**","/img/**","/plugins/**").permitAll() .antMatchers("/**").hasAnyRole("USER","SUPER","PRODUCT") .anyRequest() .authenticated() //这里就代表/**下的资源只有在认证通过之后才能访问到 .and() //这里的and是表示新的一个配置的开始 .formLogin().loginPage("/toLogin") //这个是配置的登录的页面 .loginProcessingUrl("/login") //这个是配置的对应的登录提交的请求提交 .defaultSuccessUrl("/index.html") // .successForwardUrl("/index.html") //配置登录成功后跳转的页面 .failureForwardUrl("/failer.html") //登录失败后跳转页面 .permitAll(); //释放掉处理配置的这些基本页面 /*配置下注销按钮*/ http.logout().logoutUrl("/logout") .logoutSuccessUrl("/login.html") .invalidateHttpSession(true) //这个就是配置的是否清空session .permitAll(); //这里还要再配置对应的拦截要重新配置 http.csrf().disable(); //关闭csrf }}
- 看上去很多其实对比前面的就修改了
- 这里只是将我们配置好的userService注入并导入一个加密类就完成了
@Autowiredprivate UserService userService;/** * 这个就是配置的一个将加密类加入到Spring的容器中的方式 * @return */@Beanpublic BCryptPasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder();}
- 注意这里的加密类是用这样的方式导入Bean容器的
- 上面的userService是用自动注入完成的
- 还有个小细节就是这里的用户权限都是去掉了ROLE_这个前缀的
- 我们查询出来的结果是有前缀的
到这里基本的配置就编写完成,可以运行测试了
开启方法认证
- 就是我们之前配置的只有某些角色才能访问的类
@EnableGlobalMethodSecurity(securedEnabled = true)
- 还是熟悉的有3种配置方式
- 这里就开启的SpringSecuirty中内部的注解了
- 写在我们搞的那个配置类里面
- 然后就可以给我们对应的访问方法上加权限了
- 不是太熟悉的403界面出现了
配置自定义403界面
- 这里因为直接跳转有点low
- 我们配置下
import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class MVCConfig implements WebMvcConfigurer { /** * 视图跳转解析器 * @param registry */ @Override public void addViewControllers(ViewControllerRegistry registry){ //这里就可以设置对应的视图跳转了 registry.addViewController("/error/403").setViewName("403"); registry.addViewController("/error").setViewName("500"); //其他的访问都跳转到500 }}
- 配置个页面跳转类
- 不多说那些花里胡哨的了
- 下面这个才是重点
- 编写异常配置类
import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.security.access.AccessDeniedException;//这里注意导入的AccessDeniedException是Spring的,不是java原生的那个夜异常/** * 异常拦截器 */@ControllerAdvicepublic class HandlerControllerException { /** * 还是熟悉的配置类,这里配置的就是对应会出现的异常了 */ @ExceptionHandler(RuntimeException.class) public String handException(RuntimeException e){ if(e instanceof AccessDeniedException){ return "redirect:/error/403"; } return "redirect:/error"; }}
- 当符合的时候就是出现403异常的时候
- 这样就可以跳转到对应的错误界面了
- 还有一个需要的就是这里导入的判断的包是这个
import org.springframework.security.access.AccessDeniedException;
- 不要导入错了
- 这样就能进入我们美丽的自定义403界面了