SpringSecurity - 用户动态认证

一、SpringSecurity

Spring Security 是 Spring 家族中的成员,基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。

两个主要的策略是“认证”和“授权”(或者访问控制),一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分,这两点也是 Spring Security 重要核心功能。

  • 用户认证指的是:验证某个用户是否为系统中的合法主体,也就是说用户能否访问
    该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。通俗点说就是系统认为用户是否能登录
  • 用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户
    所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。通俗点讲就是系统判断用户是否有权限去做某些事情。

SpringSecurity 对比 Shiro

SpringSecurity:

  • 和 Spring 无缝整合。
  • 全面的权限控制。
  • 专门为 Web 开发而设计。
  • 旧版本不能脱离 Web 环境使用。
  • 新版本对整个框架进行了分层抽取,分成了核心模块和 Web 模块。单独
    引入核心模块就可以脱离 Web 环境。
  • 重量级

Shiro:

  • Apache 旗下的轻量级权限控制框架。
  • 轻量级。Shiro 主张的理念是把复杂的事情变简单。针对对性能有更高要求
    的互联网应用有更好表现。
  • 通用性。
  • 好处:不局限于 Web 环境,可以脱离 Web 环境使用。
  • 缺陷:在 Web 环境下一些特定的需求需要手动编写代码定制。

二、SpringSecurity 用户认证

创建一个SpringBoot项目,在pom中引入SpringSecurity 的依赖:

org.springframework.bootspring-boot-starter-security

这里先写一个测试接口:

@RestController
public class TestController {@GetMapping("/admin/test")public String admin(){return "/admin/test success";}
}

接口中什么也没做,就返回了一个字符串,下面启动项目,在浏览器访问测试接口:http:localhost:8080/admin/test
在这里插入图片描述
可以发现竟然跳转到了一个登录页面,但是我们什么都没有做呀,是SpringSecurity 已经被SpringBoot自动集成好了,现在已经起作用了,那我们要怎么登录呢?,其实看下打印的日志,已经把密码告诉我们了:
在这里插入图片描述
用户名默认是user,再输入上面的密码,就会发现接口可以访问了:
在这里插入图片描述
现在就会发现用户名密码,并不是我们设置的,显然不符合我们的要求,这里我们可以在配制文件中把用户名密码给指定好,修改application.yml文件:

server:port: 8080
spring:security:user:name: bxcpassword: 1234

重启项目,发现使用上面配制的密码就可以登录了。
在这里插入图片描述
在这里插入图片描述
用户名和密码即使可以在配制文件中配制,显然也是不符合我们的要求的,如果我们需要多个用户呢,下面就可以集成security提供的WebSecurityConfigurerAdapter类,可以做更细粒话的用户认证操作,下面创建一个WebSecurityConfig类继承WebSecurityConfigurerAdapter

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Overridepublic void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("admin").password(new BCryptPasswordEncoder().encode("1234")).authorities("admin");auth.inMemoryAuthentication().withUser("common").password(new BCryptPasswordEncoder().encode("1234")).authorities("common");}@BeanPasswordEncoder password() {return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/**").fullyAuthenticated().and().formLogin().permitAll().and().csrf().disable();}
}

上面的配制,添加了两个用户,其中authorities表示该用户所拥有的权限或角色,现在还没有授权操作,这里可以随便给一个就可以,后面我们讲解授权的使用。
其中configure(HttpSecurity http)这个方法可以细粒度的配制验证模式和那些地址对应那些角色或权限,上面配制了所有的接口都需要进行认证才可以访问,并且认证方式是form表单的形式,并且关闭了csrf

下面再次访问上面的接口,使用上面配制的两个用户就可以实现登录了。
在这里插入图片描述
在这里插入图片描述

一般用户名和密码都是存储在数据库中的,要实现动态的用户认证,下面我们就以上面这个配制为基础继续学习,我们可以看下AuthenticationManagerBuilder所支持的认证方式:
在这里插入图片描述
可以看到有个userDetailsService方式可以传入一个UserDetailsService对象,而UserDetailsService中又有个loadUserByUsername方法,传入的参数就是用户名。在这里插入图片描述
所以这里可以创建一个自己的UserService类,实现UserDetailsService,并在loadUserByUsername中查询数据库,并将用户信息放入UserDetails对象中,返回出去就可以实现用户的动态认证了。

三、用户动态认证

既然是用户动态的认证,肯定需要从数据库中取数据,所以先创建一个数据库,并新建表user

CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(255) NOT NULL,`password` varchar(255) NOT NULL,`enabled` tinyint(1) NOT NULL,`locked` tinyint(1) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

在这里插入图片描述
下面就要引入关于数据库的依赖,这里使用mybatisplus,在pom 中添加依赖:

com.baomidoumybatis-plus-boot-starter3.3.2
mysqlmysql-connector-java
com.alibabadruid1.1.6

配制文件中添加数据库的连接信息:

server:port: 8080spring:datasource:url: jdbc:mysql://192.168.40.1:3307/testdb?useUnicode=true&characterEncoding=utf8username: rootpassword: root123type: com.alibaba.druid.pool.DruidDataSource
mybatis-plus:mapper-locations: classpath:mapper/xml/*.xmltype-aliases-package: com.bxc.securitydemo.entityconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true

创建UserEntity实现UserDetails,在loadUserByUsername方法中直接返回UserEntity就可以了:

@Data
@TableName(value = "user")
public class UserEntity implements UserDetails {private int id;private String username;private String password;@Getter(value = AccessLevel.NONE)private Boolean enabled;private Boolean locked;@TableField(exist = false)private List roles;@Overridepublic Collection getAuthorities() {return roles;}@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return username;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return !locked;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return enabled;}
}

创建UserMapper直接继承BaseMapper获取操作方法

@Mapper
@Repository
public interface UserMapper extends BaseMapper {
}

创建UserService实现UserDetailsService

@Service
public class UserService implements UserDetailsService {@AutowiredUserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {LambdaQueryWrapper wrapper = new LambdaQueryWrapper().eq(UserEntity::getUsername, username);UserEntity userEntity = userMapper.selectOne(wrapper);if (userEntity == null) {throw new UsernameNotFoundException("用户不存在!");}List auths = new ArrayList<>();SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_admin");auths.add(authority);userEntity.setRoles(auths);return userEntity;}public boolean register(String userName, String password) {UserEntity entity = new UserEntity();entity.setUsername(userName);entity.setPassword(new BCryptPasswordEncoder().encode(password));entity.setEnabled(true);entity.setLocked(false);return userMapper.insert(entity) > 0;}
}

在该类中写了两个方法,一个是UserDetailsService类需要的loadUserByUsername在登录的时候会触发该方法,另一个就是注册了,就是做了向数据库添加数据的功能。

下面修改我们的WebSecurityConfig配制文件:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserService userService;@Overridepublic void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userService).passwordEncoder(password());}@BeanPasswordEncoder password() {return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/**").fullyAuthenticated().and().formLogin().permitAll().and().csrf().disable();}@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/register/**");}
}

最后写一个注册的入口controller

@RestController
public class UserController {@AutowiredUserService userService;@PostMapping("/register")public String register(String userName, String passwd) {return userService.register(userName, passwd) ? "success" : "err";}
}

下面开始测试:
先注册一个用户bxctest,密码为123456
在这里插入图片描述
在这里插入图片描述

下面使用bxctest登录:
在这里插入图片描述
在这里插入图片描述
现在就实现了数据库动态认证了。
在这里插入图片描述
喜欢的小伙伴可以关注我的个人微信公众号,获取更多学习资料!


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部