Security之基础篇

1、功能实现

创建内存用户,进行登录
访问权限接口

2、security01 子工程

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.yzm</groupId>
        <artifactId>security</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
    </parent>

    <artifactId>security01</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>security01</name>
    <description>Demo project for Spring Boot</description>

    <dependencies>
        <dependency>
            <groupId>com.yzm</groupId>
            <artifactId>common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

由于还没有用到数据库,启动类 exclude 数据库自动配置,以免启动报错

package com.yzm.security01;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class Security01Application {

    public static void main(String[] args) {
        SpringApplication.run(Security01Application.class, args);
    }
}

3、SecurityConfig 配置类

package com.yzm.security01.config;

import lombok.extern.slf4j.Slf4j;
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;
import org.springframework.security.crypto.password.PasswordEncoder;

@Slf4j
@Configuration
@EnableWebSecurity // 开启 Security 服务
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 密码编码器
     * passwordEncoder.encode是用来加密的,passwordEncoder.matches是用来解密的
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 配置用户,这里是创建内存用户
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 从内存创建用户
        auth.inMemoryAuthentication()
                .withUser("admin")
                .password(passwordEncoder().encode("123456"))
                // 基于内存创建的用户不能同时使用roles和authorities,如果同时使用只有后面的生效,这个坑
                .roles("ADMIN", "USER")
                //.authorities("select", "delete")
                .and()
                .withUser("yzm")
                .password(passwordEncoder().encode("123456"))
                .roles("USER")
                //.authorities("select")
        ;

    }

    /**
     * http安全配置
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // 关闭CSRF跨域
                .csrf().disable()

                // 默认登录
                .formLogin().permitAll()
                .and()

                // 退出登录
                .logout().permitAll()
                .and()

                // 访问路径URL的授权策略,如注册、登录免登录认证等
                .authorizeRequests()
                .antMatchers("/home", "/").permitAll() //指定url放行
                .antMatchers("/user/**").hasAnyRole("ADMIN", "USER") // 需要角色(二选一)
                .antMatchers("/admin/**").hasRole("ADMIN") // 需要角色
                .anyRequest().authenticated() //其他任何请求都需要身份认证
                .and()
        ;
    }
}

4、访问接口

package com.yzm.security01.controller;

import com.alibaba.fastjson.JSONObject;
import com.yzm.common.entity.HttpResult;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HomeController {

    @GetMapping(value = {"/", "/home"})
    public Object home() {
        return "home";
    }

    @GetMapping("/hello")
    @ResponseBody
    public Object hello() {
        return "hello";
    }

    // 通过authentication或userDetails获取当前登录用户信息+
    @GetMapping(value = {"/user", "/admin"})
    @ResponseBody
    public String info(Authentication authentication, @AuthenticationPrincipal UserDetails userDetails) {
System.out.println("authentication :");
        System.out.println(JSONObject.toJSONString(authentication, true));
        System.out.println("userDetails :");
        System.out.println(JSONObject.toJSONString(userDetails, true));
        return "请求成功";
    }

    @GetMapping(value = {"/user/select", "/admin/select"})
    @ResponseBody
    public Object select() {
        return "Select";
    }

    @GetMapping(value = {"/user/create", "/admin/create"})
    @ResponseBody
    public Object create() {
        return "Create";
    }

    @GetMapping(value = {"/user/update", "/admin/update"})
    @ResponseBody
    public Object update() {
        return "Update";
    }

    @GetMapping(value = {"/user/delete", "/admin/delete"})
    @ResponseBody
    public Object delete() {
        return "Delete";
    }
}

5、首页,访问链接,便于测试

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h2>
    <a href="/hello"> hello </a>
</h2>

<h3>user</h3>
<h4>
    <a href="/user">User角色</a>
</h4>
<p><a href="/user/select">User角色,拥有 select 权限</a></p>
<p><a href="/user/create">User角色,拥有 create 权限</a></p>
<p><a href="/user/update">User角色,拥有 update 权限</a></p>
<p><a href="/user/delete">User角色,拥有 delete 权限</a></p>

<h3>admin</h3>
<h4>
    <a href="/admin">Admin角色</a>
</h4>
<p><a href="/admin/select">Admin角色,拥有 select 权限</a></p>
<p><a href="/admin/create">Admin角色,拥有 create 权限</a></p>
<p><a href="/admin/update">Admin角色,拥有 update 权限</a></p>
<p><a href="/admin/delete">Admin角色,拥有 delete 权限</a></p>
</body>
</html>

6、测试 roles 权限

拥有用户:
admin --> ADMIN、USER
yzm --> USER

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 从内存创建用户
        auth.inMemoryAuthentication()
                .withUser("admin")
                // 密码需要加密,不加就提示错误
                .password(passwordEncoder().encode("123456"))
                // 基于内存创建的用户不能同时使用roles和authorities,如果同时使用只有后面的生效,这个坑
                .roles("ADMIN", "USER")
                //.authorities("select", "delete")
                .and()
                .withUser("yzm")
                .password(passwordEncoder().encode("123456"))
                .roles("USER")
                //.authorities("select")
        ;

    }

启动项目 访问 / 或 /home 首页接口,由于是放行的,所以可以访问
在这里插入图片描述

点击 hello 跳转到Security默认提供的登录页面
在这里插入图片描述

.authorizeRequests()
     .antMatchers("/home", "/").permitAll() //指定url放行
     .antMatchers("/user/**").hasAnyRole("ADMIN", "USER") // 需要角色(二选一)
     .antMatchers("/admin/**").hasRole("ADMIN") // 需要角色
     .anyRequest().authenticated() //其他任何请求都需要身份认证
     .and()

在前面我们没有拦截/hello,它走的是需要认证 即.anyRequest().authenticated()
没有认证的请求,默认转发到 /login

开始登录yzm
在这里插入图片描述
在这里插入图片描述
登录成功之后,继续之前的 /hello 请求

如果我们创建内存用户不是使用加密的,而是直接明文
把.password(passwordEncoder().encode(“123456”)) 改成 .password(“123456”)
那么我们点击登录,就会提示下面的错误
在这里插入图片描述
在这里插入图片描述
提示 Encoded password does not look like BCrypt (密码没有加密)

回到首页,此时登录用户:yzm,能访问部分接口
在这里插入图片描述
点击 user角色 可以查看当前登录的用户信息

authentication :
{
	"authenticated":true,
	"authorities":[
		{
			"authority":"user:select"
		}
	],
	"details":{
		"remoteAddress":"0:0:0:0:0:0:0:1",
		"sessionId":"20A8546453B14E0C3B066A8150B44B9D"
	},
	"name":"yzm",
	"principal":{
		"accountNonExpired":true,
		"accountNonLocked":true,
		"authorities":[{"$ref":"$.authorities[0]"}],
		"credentialsNonExpired":true,
		"enabled":true,
		"username":"yzm"
	}
}
userDetails :
{
	"accountNonExpired":true,
	"accountNonLocked":true,
	"authorities":[{
		"authority":"user:select"
	}],
	"credentialsNonExpired":true,
	"enabled":true,
	"username":"yzm"
}

Authentication 用户认证对象 ,内容比较多并且包含了 UserDetails信息
UserDetails 用户详情对象

访问 localhost:8080/logout 退出登录
在这里插入图片描述

登录admin,admin有双重身份,可以访问所有的接口

7、测试 authorities 权限

admin --> admin:select、admin:delete
yzm --> user:select

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("admin")
                .password(passwordEncoder().encode("123456"))
                // 基于内存创建的用户不能同时使用roles和authorities,如果同时使用只有后面的生效,这个坑
                //.roles("ADMIN", "USER")
                .authorities("admin:select", "admin:delete")
                .and()
                .withUser("yzm")
                .password(passwordEncoder().encode("123456"))
                //.roles("USER")
                .authorities("user:select")
        ;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                ...

                .antMatchers("/user/select").hasAuthority("user:select") // 需要权限
                .antMatchers("/user/delete").hasAuthority("user:delete")
                .antMatchers("/admin/select").hasAuthority("admin:select")
                .antMatchers("/admin/delete").hasAnyAuthority("admin:delete", "admin:remove") // 需要权限(二选一)
                .anyRequest().authenticated() //其他任何请求都需要身份认证
                .and()

        ;
    }

重启项目
在这里插入图片描述

8、问题

.withUser("admin")
      // 密码需要加密,不加就提示错误
      .password(passwordEncoder().encode("123456"))
      // 基于内存创建的用户不能同时使用roles和authorities,如果同时使用只有后面的生效,这个坑
      //.roles("ADMIN", "USER")
      .authorities("admin:select", "admin:delete")

在上面的代码中 roles() 跟 authorities() 能不能同时设置?
可以同时设置,但只有最后设置的那个有效,后面设置的会替换掉前面设置的
我们可以看下它们的实现

        public UserDetailsManagerConfigurer<B, C>.UserDetailsBuilder roles(String... roles) {
            this.user.roles(roles);
            return this;
        }

        public UserDetailsManagerConfigurer<B, C>.UserDetailsBuilder authorities(GrantedAuthority... authorities) {
            this.user.authorities(authorities);
            return this;
        }

		public UserBuilder roles(String... roles) {
			List<GrantedAuthority> authorities = new ArrayList<>(roles.length);
			for (String role : roles) {
				Assert.isTrue(!role.startsWith("ROLE_"),
						() -> role + " cannot start with ROLE_ (it is automatically added)");
				authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
			}
			// roles最后是调用authorities()方法的
			return authorities(authorities);
		}

		public UserBuilder authorities(GrantedAuthority... authorities) {
			return authorities(Arrays.asList(authorities));
		}

		public UserBuilder authorities(Collection<? extends GrantedAuthority> authorities) {
			this.authorities = new ArrayList<>(authorities);
			return this;
		}

可以看到roles和authorities最后都是给authorities赋值,使用户拥有对应的角色权限
所以我们一般设置一个就可以了

相关链接

首页
下一篇:入门篇

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值