Spring Security使用数据库登录认证授权

网友投稿 266 2022-09-05


Spring Security使用数据库登录认证授权

目录一、搭建项目环境1、创建 RBAC五张表2、创建项目二、整合 Spring Security实现用户认证1、后端整合2、前端整合三、整合 Spring Security实现用户授权1、后端2、前端

一、搭建项目环境

1、创建 RBAC五张表

RBAC,即基于角色的权限访问控制(Role-Based Access Control),就是用户通过角色与权限进行关联。在这种模型中,用户与角色之间,角色与权限之间,一般者是多对多的关系。

在 mysql数据库中,创建如下几个表:

DROP TABLE IF EXISTS sys_user;

CREATE TABLE sys_user(

id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键id' ,

username VARCHAR(60) COMMENT '用户名' ,

password VARCHAR(255) COMMENT '密码' ,

status tinyint(1) COMMENT '用户状态,1-开启-0禁用' ,

password_non_expired tinyint(1) COMMENT '密码是否失效,1-可用,0-失效' ,

PRIMARY KEY (id)

) COMMENT = '用户表';

DROP TABLE IF EXISTS sys_role;

CREATE TABLE sys_role(

id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键id' ,

role_name VARCHAR(64) NOT NULL COMMENT '角色名' ,

role_desc VARCHAR(64) NOT NULL COMMENT '角色描述' ,

PRIMARY KEY (id)

) COMMENT = '角色表';

DROP TABLE IF EXISTS sys_permission;

CREATE TABLE sys_permission(

id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键id' ,

parent_id BIGINT COMMENT '父id' ,

permission_name VARCHAR(64) COMMENT '菜单名称' ,

permission_url VARCHAR(255) COMMENT '菜单地址' ,

PRIMARY KEY (id)

) COMMENT = '权限表';

DROP TABLE IF EXISTS sys_user_role;

CREATE TABLE sys_user_role(

id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键id' ,

user_id BIGINT NOT NULL COMMENT '用户id' ,

role_id BIGINT COMMENT '角色id' ,

enabled tinyint(1) DEFAULT 1 COMMENT '是否有效' ,

PRIMARY KEY (id)

) COMMENT = '用户角色关联表';

DROP TABLE IF EXISTS sys_role_permission;

CREATE TABLE sys_role_permission(

id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键id' ,

role_id BIGINT NOT NULL COMMENT '角色id' ,

permission_id BIGINT COMMENT '权限id' ,

PRIMARY KEY (id)

) COMMENT = '角色权限表';

2、创建项目

创建 Mavne 项目,Springboot + Spring Security + MyBatis + MySQL + jsp。

1)配置文件如下

server:

port: 9090

# jsp配置

spring:

mvc:

view:

prefix: /pages/

suffix: .jsp

datasource:

driver-class-name: com.mysql.cj.jdbc.Driver

url: jdbc:mysql://localhost:3306/security_authority?useUnicode=true;characterEncoding=utf8;useSSL=true;serverTimezone=GMT

username: root

password: 123456

# mybatis配置

mybatis:

configuration:

map-underscore-to-camel-case: true

mapper-locations: classpath:mybatis/mapper/*.xml

logging:

level:

com.charge.learn.springsecurity.springboot.security.jsp.dao: debug

2)启动类

@SpringBootApplication

@MapperScan("com.charge.learn.springsecurity.springboot.security.jsp.dao")

public class SpringSecurityApplication {

public static void main(String[] args) {

SpringApplication.run(SpringSecurityApplication.class, args);

}

}

二、整合 Spring Security实现用户认证

1、后端整合

1.1 用户

Spring Security的用户对象是 UserDetail类型,Spring Security在认证流程中只认 UserDetail用户。

通过 UserDetailsService的 loadUserByUsername方法获取 UserDetail用户。认证成功之后,调用的是这个带有三个参数的 UsernamePasswordAuthenticationToken构造方法,将 角色信息添加到了ArrayList集合中。在 successfulAuthentication 方法中,将认证信息存储到了SecurityContext中。

UserDetail接口的方法(根据用户业务来处理这几个值)。

boolean enabled 账户是否可用boolean accountNonExpired 账户是否失效boolean credentialsNonExpired 账户密码是否失效boolean accountNonLocked 账户是否锁定Collection extends GrantedAuthority> getAuthorities() 获取账户的所有权限(用户角色)

注意:四个布尔类型的参数都为 true时,然后成功,否则,有一个为 false,就会认证失败。

所以,我们可以将我们的用户封装成 UserDetail对象。这里我们让用户对象实现 UserDetail接口,那么我们用户就属于 UserDetail类型的用户,然后实现接口的方法。

public class SysUser implements UserDetails {

private Long id;

private String username;

private String password;

private Boolean status; //用户状态,1-开启-0禁用

private Boolean passwordNonExpired; //密码是否失效,1-可用,0-失效

/**

* 用户关联的所有角色

*/

private List roles = new ArrayList<>();

//get、set方法

//标记该字段不做json处理

@JsonIgnore

@Override

public Collection extends GrantedAuthority> getAuthorities() {

return roles;

}

@JsonIgnore

@Override

public boolean isAccountNonExpired() {

return true;

}

@JsonIgnore

@Override

public boolean isAchttp://countNonLocked() {

return true;

}

@JsonIgnore

@Override

public boolean isCredentialsNonExpired() {

return passwordNonExpired == null ? false : DRBqzdpasswordNonExpired;

}

@JsonIgnore

@Override

public boolean isEnabled() {

return status == null ? false : status;

}

}

1.2 角色

Spring Security的权限对象是 GrantedAuthority类型。通过它的值来实现权限管理的。

所以,我们让角色对象实现GrantedAuthority接口,那么我们角色就属于 GrantedAuthority类型,然后实现接口的方法。上面用户可以直接将 角色添加到 Collection extends GrantedAuthority>集合中。

public class SysRole implements GrantedAuthority {

private Long id;

private String roleName;

private String roleDesc;

//get、set方法

//标记该字段不做json处理

@JsonIgnore

@Override

public String getAuthority() {

return roleName;

}

}

1.3 SysUserService接受继承UserDetailsService类

Spring Security在认证流程中通过 UserDetailsService的 loadUserByUsername方法获取 UserDetail用户。

所以,我们让 UserService接口继承 UserDetailsService类,然后重写 loadUserByUsername方法。

在 loadUserByUsername方法中,获取我们的用户信息( UserDetail类型),记得将用户关联的角色也赋值为用户信息。

public interface SysUserService extends UserDetailsService {

void save(SysUser user);

}

@Service

@Transactional

public class SysUserServiceImpl implements SysUserService {

@Autowired

private SysUserMapper sysUserMapper;

@Autowired

private SysRoleMapper sysRoleMapper;

@Autowired

private BCryptPasswordEncoder passwordEncoder;

@Override

public void save(SysUser sysUser) {

// 将密码加密入库

sysUser.setPassword(passwordEncoder.encode(sysUser.getPassword()));

sysUserMapper.insert(sysUser);

}

/**

* 认证业务

*

* @param username

* - 用户在浏览器输入的用户名

* @return UserDetails - Spring Security的用户对象,返回 null表示认证失败!

* @throws UsernameNotFoundException

*/

@Override

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

/**

* 用户信息和角色信息可以一步关联查询到位得到SysUser,我这里分开查询

*/

// 1.查询用户

SysUser sysUser = sysUserMapper.getByUsername(username);

if (sysUser == null) {

return null;

}

// 2.获取用户关联的所有角色

List sysRoles = sysRoleMapper.listAllByUserId(sysUser.getId());

sysUser.setRoles(sysRoles);

System.out.println("====> sysUser=" + sysUser.toString());

return sysUser;

}

}

mapper.xml中的几个方法

select id, username, password, status, password_non_expired from sys_user where username = #{username}

SELECT r.id, r.role_name role_name, r.role_desc role_desc

FROM sys_role r, sys_user_role ur

WHERE r.id = ur.role_id AND ur.user_id = #{userId}

1.4 创建 SpringSecurity配置类

自定义一个配置类,添加@EnableWebSecurity注解,并继承WebSecurityConfigurerAdapter类。然后就拥有了 SpringSecutiry的所有默认配置。我们也可以修改配置。

@Configuration

@EnableWebSecurity

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired

private SysUserService userService;

// 加密对象注入IOC容器

@Bean

public BCryptPasswordEncoder passwordEncoder(){

return new BCryptPasswordEncoder();

}

// 1.指定认证对象的来源(内存或者数据库),指定加密方式

@Override

public void configure(AuthenticationManagerBuilder auth) throws Exception {

auth.userDetailsService(userService).passwordEncoder(passwordEncoder());

}

//2. SpringSecurity配置相关信息

@Override

public void configure(HttpSecurity http) throws Exception {

// 释放静态资源,指定拦截规则,指定自定义的认证和退出页面,csrf配置等

http.authorizeRequests()

// 指定拦截规则

.antMatchers("/login.jsp", "failer.jsp", "/css/**", "/img/**", "/plugins/**").permitAll() //释放这些资源,不拦截

.antMatchers("/**").hasAnyRole("USER", "ADMIN") //所有资源都需要这些角色中的一个

.anyRequest().authenticated() //其他请求,必须认证通过之后才能访问

.and() // 表示新的一个配置开始

// 指定自定义的认证页面

.formLogin()

.loginPage("/login.jsp")

.loginProcessingUrl("/login")

.successForwardUrl("/index.jsp")

.failureForwardUrl("/failer.jsp")

.permitAll() // 释放这些资源,不拦截登录

.and()

// 指定自定义的退出页面

.logout()

.logoutSuccessUrl("/logout")

.invalidateHttpSession(true) // 清楚session

.logoutSuccessUrl("/login.jsp")

.permitAll()

//.and()

// 禁用csrf配置,默认开启的(一般不写,页面要加csrf),这里我们测试下

// .and()

// .csrf()

// .disable()

;

}

主要配置信息如下:

指定认证对象 SysUserService (UserDetailsService类型)指定了用户密码使用的加密对象SpringSecurity配置相关信息,比如:指定拦截规则,指定自定义的认证页面,csrf等。

2、前端整合

在 Spring Security 中,如果我们不做任何配置,默认的登录页面和登录接口的地址都是 /login,即默认会存在如下两个请求:

GET http://localhost:8080/loginPOST http://localhost:8080/login

如果是 GET 请求表示你想访问登录页面,如果是 POST 请求,表示你想提交登录数据。默认的表单字段为 username和password。

SpringSecurity 默认 是开启 csrf防护机制。所以,在自定义的表单上添加上 _csrf隐藏input(必须要写在form表单里面)。

引入 security标签库

<%@taglib uri="http://springframework.org/security/tags" prefix="security"%>

启动项目,登录认证访问ok.

三、整合 Spring Security实现用户授权

认证过程获取用户信息时,我们已经把用户关联的角色信息设置到了 UserDetails中,所以,我们只需要分配 资源访问的角色就可以了。

1、后端

1.1 开启 Spring Security权限控制

Spring Security可以通过注解的方式来控制类或者方法的访问权限。支持开启权限控制的注解类型如下:

jsr250-annotations:表示支持 jsr250-api的注解pre-post-annotations:表示支持 spring表达式注解secured-annotations:这才是 Spring Security提供的注解

在实际开发中,用一类即可,三个都开启也没关系。在 SpringSecurity配置类上 添加 @EnableGlobalMethodSecurity注解,表示开启 Spring Security权限控制,这里我们三类都开启了。

@Configuration

@EnableWebSecurity

@EnableGlobalMethodSecurity(securedEnabled=true, prePostEnabled = true, jsr250Enabled = true)

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

...

@Configuration

@EnableWebSecurity

@EnableGlobalMethodSecurity(securedEnabled=true, prePostEnabled = true, jsr250Enabled = true)

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

...

1.2 在角色对应类或者方法上添加权限注解

1.2.1 使用 Spring Security注解

@Secured({“ROLE_ADMIN”,“ROLE_PRODUCT”})

@Controller

@RequestMapping("/user")

@Secured("ROLE_ADMIN") //表示当前类中所有方法需要 ROLE_ADMIN才能访问

public class UserController {

@Autowired

private UserService userService;

@RequestMapping("/findAll")

public String findAll(Model model){

List list = userService.findAll();

model.addAttribute("list", list);

return "user-list";

}

。。。

}

1.2.2 使用 Spring表达式注解

@PreAuthorize(“hasAnyRole(‘ROLE_ADMIN’,‘ROLE_PRODUCT’)”)

@Controller

@RequestMapping("/product")

public class ProductController {

@RequestMapping("/findAll")

//表示当前类中findAll方法需要 ROLE_ADMIN或者 ROLE_PRODUCT才能访问

@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_PRODUCT')")

public String findAll(){

return "product-list";

}

}

1.2.3 使用 JSR-250注解

@RolesAllowed({“ROLE_ADMIN”,“ROLE_User”})

@Controller

@RequestMapping("/order")

@RolesAllowed({"ROLE_ADMIN","ROLE_USER"}) //表示当前类中所有方法都需要ROLE_ADMIN或者ROLE_User才能访问

public class OrderController {

@RequestMapping("/findAll")

public String findAll(){

return "order-list";

}

}

2、前端

在jsp业页面中,对每个菜单资源通过 SpringSecurity标签库指定访问所需的角色。

<%@taglib uri="http://springframework.org/security/tags" prefix="security" %>

启动项目,通过不同的用户登录,授权访问ok.


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:【原子搜索算法】基于原子搜索算法求解单目标优化问题附matlab代码
下一篇:【樽海鞘算法】基于多子群的共生非均匀高斯变异樽海鞘群算法求解单目标优化问题附matlab代码MSNSSA
相关文章

 发表评论

暂时没有评论,来抢沙发吧~