安全是一个不断变化的目标,追求一个全面的、系统范围的方法很重要。在安全领域,我们鼓励您采用“安全层”,这样每个层都可以尽可能地保证自身的安全性,并且连续的层提供额外的安全性。每一层的安全性越“严格”,您的应用程序就越健壮和安全。在底层,为了减少中间人攻击,你需要处理诸如传输安全和系统辨识等问题。接下来,您将通常使用防火墙,也许是通过 vpn 或 IP 安全性来确保只有经过授权的系统才能尝试连接。在公司环境中,您可以部署 DMZ 来将面向公共的服务器与后端数据库和应用程序服务器分开。您的操作系统也将发挥关键作用,解决诸如作为非特权用户运行进程和最大化文件系统安全性等问题。操作系统通常也会配置自己的防火墙。希望在某个地方,你可以尝试阻止针对系统的分布式拒绝服务攻击攻击和暴力破解。入侵预防系统安全协议对于监控和响应攻击也特别有用,这样的系统能够采取保护措施,比如实时阻止违规的 TCP/IP 地址。转移到较高的层,您的 Java 虚拟机有望被配置为最小化授予不同 Java 类型的权限,然后您的应用程序将添加自己的问题域特定的安全配置。Spring Security 使后一个领域——应用程序安全性——更加容易。
人们使用 Spring Security 有很多原因,但是大多数人是在发现 Java EE 的 Servlet 规范或 EJB 规范的安全特性之后才开始使用这个项目的,这些特性缺乏典型的企业应用场景所需要的深度。虽然提到了这些标准,但重要的是要认识到它们在 WAR 或 EAR 级别上是不可移植的。因此,如果切换服务器环境,在新的目标环境中重新配置应用程序的安全性通常需要做大量工作。使用 Spring Security 克服了这些问题,并且还为您带来了许多其他有用的、可定制的安全特性。
您可能知道应用程序安全性的两个主要方面是“身份验证”和“授权”(或“访问控制”)。这是 Spring Security 的两个主要目标。“身份验证”是建立一个主体的过程,这个主体就是他们声称的那个人(“主体”通常指一个用户、设备或者其他能够在你的应用程序中执行某个操作的系统)。“授权”是指决定是否允许主体在应用程序中执行操作的过程。为了达到需要作出授权决定的地步,认证过程已经确定了主体的身份。这些概念是常见的,而且根本不是 Spring Security 特有的。
(自己看的时候,蛮喜欢就加在这篇文章的开头拉。)
文章是我在security 5.0.5. RELEASE 版本文档里看到的 。
https://docs.spring.io/spring-security/site/docs/5.0.5.RELEASE/reference/htmlsingle/
本文讲的是Spring Boot集成Spring-Security 框架。内容不牵扯到过多具体原理讲解,只是做了简单概述。
适合于初学者,状态大概:对这个不是很懂,但是暂时需要在项目中使用Spring-Boot-Security。
案例里有具体的数据库角色表 、权限表、资源表 是可以切入到已有项目的。
而且是添加注解的方式。
流程、原理等等。我应该也会去写滴,相信我… 对这个东东流程、原理,还是非常好奇的。
当然看完就好奇的就马上去Debug吧 奥利给!!!
项目中有一行代码用到了 jdk 8 中的 Optional 我之前写了文章讲解
这正好是一次实用。 大家可以看一看。
https://blog.csdn.net/weixin_45821811/article/details/115656637
我之前已经写完表的文章。如下
https://blog.csdn.net/weixin_45821811/article/details/115737401
工程结构图
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
</dependencies>
server:
port: 8787
spring:
datasource: #自己的数据库名 自己的数据库密码
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useSSL=false&characterEncoding=utf8&serverTimezone=GMT
password: 123456
username: root
WebConfig
@Configuration // 相当于springmvc 文件
public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("redirect:/login");
}
}
WebSecurityConfig 安全配置
@Configuration
@EnableWebSecurity // 开启WebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 这个开启权限注解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable(). //屏蔽csrf
authorizeRequests()
// .antMatchers("/r/r1").hasAnyAuthority("p1") // 这种是没有开启权限注解 没有添加权限注解的方法
// .antMatchers("/r/r2").hasAnyAuthority("p2")
// .antMatchers("/r/r3").hasAnyAuthority("p1","p2")
.antMatchers("/r/**").authenticated()
.anyRequest().permitAll()
.and()
.formLogin()
.permitAll()
.and()
.logout()
.logoutUrl("/logout") // 退出url
.logoutSuccessUrl("/logout-success"); // 自定义登录成功的配置 成功后 转向这个url
}
}
MyUser 用户
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)// 链式
public class MyUser {
private String id;
private String username;
private String password;
private String fullname;
private String mobile;
}
PermissionDto 资源权限
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)// 链式
public class PermissionDto {
private String id;
private String code;
private String description;
private String url;
}
@Mapper
public interface MyUserDao {
@Select("select * from user_db where username=#{username}")
Optional<MyUser> getUserByName(String username);
// 这里是多表联查 查询当前角色拥有的权限 表的结构在我另一篇文章有说明
@Select("SELECT * FROM t_permission WHERE id IN( SELECT permission_id FROM t_role_permission WHERE role_id IN(SELECT role_id FROM t_user_role WHERE user_id = #{userId} ))")
List<PermissionDto> findPermissionsByUserId(String userId);
}
@Configuration
public class SpringDataMyUserDetailsService implements UserDetailsService {
@Autowired
MyUserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<MyUser> user = userDao.getUserByName(username);
if (user.isPresent()) {// 判空操作 这个是jdk8的一个新特性是 在我一篇文章中有讲
MyUser myUser = user.get(); // 取出操作
List<PermissionDto> list = userDao.findPermissionsByUserId(myUser.getId());
System.out.println("list==>" + list.toString());
//list==>[PermissionDto(id=1, code=p1, description=测试资源1, url=/r/r1)]
List<String> permissions = new ArrayList<>();
list.forEach(p -> permissions.add(p.getCode()));
System.out.println("permissions===>" + permissions.toString());
//permissions===>[p1]
// 这里之所以转成数组 是因为 构建 UserDetails 时
//.authorities(perarray) 这个添加权限的地方是可以添加一个数组的 更加方便我们个性定
//参数:权限–此用户的权限(即ROLE_USER,ROLE_ADMIN等)。 不能为空或包含空值
// public UserBuilder authorities(String... authorities) {
// return authorities(AuthorityUtils.createAuthorityList(authorities));
// }
String[] perarray = new String[permissions.size()];
permissions.toArray(perarray);
//创建userDetails
UserDetails userDetails =
User.withUsername(myUser.getUsername()).password( BCrypt.hashpw(myUser.getPassword(), BCrypt.gensalt()) ).authorities(perarray).build();
return userDetails;
}
return null;
}
}
@RestController
public class LoginController {
//后端这返回类型 大家都知道吧 我这里是写的文本 json 是application/json 这种 还有这请求格式 我大都是复制 就不是那种restful 风格拉
// 大家使用 自行修改哦
@RequestMapping(value = "/login-success", produces = "text/plain;charset=utf-8")
public String loginSuccess(){
return getUsername()+"登录成功";
}
@RequestMapping(value = "/logout-success", produces = "text/plain;charset=utf-8")
public String logout(){
return "退出成功";
}
@RequestMapping(value = "/r/r1", produces = "text/plain;charset=utf-8")
// @PreAuthorize("isAnonymous()")// 匿名访问
@PreAuthorize("hasAuthority('p1')" )
public String r1(){
return getUsername()+"资源1";
}
@RequestMapping(value = "/r/r2", produces = "text/plain;charset=utf-8")
@PreAuthorize(" hasAuthority('p2')" )
public String r2(){
return getUsername()+"资源2";
}
@RequestMapping(value = "/r/r3", produces = "text/plain;charset=utf-8")
@PreAuthorize("hasAuthority('p1') or hasAuthority('p2')" )
public String r3(){
return getUsername()+"资源3";
}
// 这个是只要认证通过都可以访问 不需要主体带有什么权限
@RequestMapping(value = "/r/r4", produces = "text/plain;charset=utf-8")
public String r4(){
return getUsername()+"资源4";
}
private String getUsername(){
String username=null;
//当前通过的用户身份
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Object principal = authentication.getPrincipal();
if (principal==null){
username="匿名";
}
if(principal instanceof UserDetails){
UserDetails userDetails= (UserDetails)principal;
username = userDetails.getUsername();
}else{
username= principal.toString();
}
return username;
}
}
@SpringBootApplication
@MapperScan("com.wyh.dao")
public class SecuritySpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SecuritySpringbootApplication.class, args);
}
}
接下来就是测试哩。还有我的自言自语…
认证成功
认证错误
每天记录一下自己的生活 也开始习惯每天这里写写 那里写写的感觉。
写这些一开始是想着方便自己看 但是写着写着就开始想要人来关注来看。
比起一开始随随便便写 现在的话要好一些拉。
就突然想把自己的知道的东西都记录下来,可能是想让人知道吧。
需要学的东西多的多啊。
继续加油哦。