Spring-Security访问控制

Spring-Security,访问控制安全框架

代码链接

  • 代码01:https://gitee.com/daltonwang/ssm-learning/tree/master/spring-security

简单介绍

能够为企业应用提供安全访问控制的安全框架——由Spring提供;

访问控制的几种实现方式:

  • Aop、拦截器实现
  • 框架实现:Spring-Security,Apache Shiro

简单使用

依赖引入

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

引入依赖并且运行SpringBoot项目之后,控制台将能够看见默认生成的密码,默认账号为user
在这里插入图片描述

修改密码

在配置文件src/main/resources/application.properties中可修改默认的账号密码

spring.security.user.name=admin
spring.security.user.password=123456

密码加密

密码如果是明文存储则会有巨大的风险,因此需要将明文→密文,然后存储

使用Spring-Security提供的实现类加密

security包中建配置类,并通过控制反转新建用于加密的类

@Configuration
public class SecurityConfig {
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

在测试类中测试两个方法encode加密方法和matches明文与密文的比较方法

@SpringBootTest
public class SecurityConfigTest {
    @Autowired
    PasswordEncoder passwordEncoder;
    @Test
    public void testSecurity(){
        String encode = passwordEncoder.encode("123456");
        System.out.println(encode);
    }
    @Test
    public void testSecurityMatch(){
        boolean matches = passwordEncoder.matches("123456", "$2a$10$a2fwphTQmI/EwTGkxOkVMOIoaihqfEaVOs6nCr9H6fCcbLkqFziDC");
        System.out.println(matches);
    }
}

加密的简单写法

在配置文件src/main/resources/application.properties中的使用密文,并在密码的头部添加{bcrypt}

spring.security.user.name=admin
spring.security.user.password={bcrypt}$2a$10$a2fwphTQmI/EwTGkxOkVMOIoaihqfEaVOs6nCr9H6fCcbLkqFziDC

此时的加密配置类

@Configuration
public class SecurityConfig {}

权限控制

即不同的用户具有不同的访问权限

配置类中配置用户和权限

注解@EnableGlobalMethodSecurity(prePostEnabled = true):告知Spring-Security开启方法权限控制
配置文件SecurityConfig继承WebSecurityConfigurerAdapter,在类中重写configure()方法
在方法中利用inMemoryAuthentication()创建临时测试用户Tom,为用户分配加密的密码“123456”,并且分配一个测试权限“/user/get”,表示Tom用户拥有访问“/user/get”的权限。

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("Tom")
                .password("{bcrypt}$2a$10$a2fwphTQmI/EwTGkxOkVMOIoaihqfEaVOs6nCr9H6fCcbLkqFziDC")
                .authorities("/user/get","/user/list");
    }
}

使用@PreAuthorize("hasAuthority('/user/get')")约定所拥有的权限

@RestController
@RequestMapping("/portal/user")
public class UserController {
    private IUserService userService;

    @Autowired
    public void setUserService(IUserService userService) {
        this.userService = userService;
    }

    @GetMapping("/get")
    @PreAuthorize("hasAuthority('/user/get')")
    public User get(Integer id){ return userService.getById(id); }

    @GetMapping("/list")
    @PreAuthorize("hasAuthority('/user/list')")
    public List<User> list(){ return userService.list(); }
}

通过存储的数据进行认证和访问控制

直接将权限和密码放于代码之中是不合理的,因此应该通过数据库等进行存储,需要时再取出来。

Spring-Security认证时候会将用户名传递到UserDetalisServiceloadUserByUsername方法获取用户信息。
如下代码就实现了通过用户名得到相应的用户信息。

@Component
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    IUserService userService;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userService.getUserDetails(username);
    }
}

引用上述方法,将得到的用户信息传入认证管理构建器中即可进行用户认证

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    UserDetailsServiceImpl userDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }
}

从数据库中获取用户数据并且构建UserDetails的过程如下:

public interface IUserService extends IService<User> {
    UserDetails getUserDetails(String username);
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    UserMapper userMapper;

    @Override
    public UserDetails getUserDetails(String username) {
        User user = userMapper.findUserByUserName(username);
        if (user==null) { return null; }
        List<Permission> userPermissionsById = userMapper.findUserPermissionsById(user.getId());
        String[] permissions = new String[userPermissionsById.size()];
        int i = 0;
        for (Permission permission : userPermissionsById) {
            permissions[i++] = permission.getAuthority();
            System.out.println(permission.getAuthority());
        }
        UserDetails userDetails = org.springframework.security.core.userdetails.User.builder()
                .username(user.getUsername())
                .password(user.getPassword())
                .accountLocked(user.getIsLocked()==1)
                .disabled(user.getIsEnabled()==0)
                .authorities(permissions)
                .build();
        return userDetails;
    }
}

认证授权配置

即控制网站资源的可访问范围,一些资源无需登录即可访问,一些资源需要登录才能访问。

Spring Security 怎么保证所有向 Spring application 发送请求的用户必须先通过认证?怎么保证用户可以通过表单或者 http 的方式进行认证。解决的办法是Spring Security中有个WebSecurityConfigurerAdapter类,程序员通过继承这个类并重写 configure(HttpSecurity http) 方法就可以按照场景需求自定义认证和授权。

默认情况下网站的全部资源都被Spring-Security保护起来,所有的资源都必须登录才能查看。默认的源码如下:

// 这是源码中默认的认证和授权的配置。
protected void configure(HttpSecurity http) throws Exception {
	http.authorizeRequests() //对请求Requests进行授权authorize
		.anyRequest().authenticated() //所有请求都需要通过认证
		.and().formLogin() //允许用户采用表单登录的方式进行认证
		.and().httpBasic(); //允许用户采用 HTTP 基本的认证方式进行认证
}

自定义登录页面认证配置

当配置表单登录后,就需要一个登录页面供用户填写 用户名 和 密码。Spring Secrity 默认了一个页面,如果你觉得它很丑,也可以自己写一个,使用 loginPage("/login")声明自定义的登录页面所在位置。当用户第一次访问 web 应用时会自动跳转到默认登录界面或者用户自定义的登录界面。

protected void configure(HttpSecurity http) throws Exception {
	http.authorizeRequests().anyRequest().authenticated() //任何请求都要经过认证
		.and()
		.formLogin().loginPage("/login").permitAll(); //指定登录页的URL,并允许所有的用户(包括没认证的)访问登录页
}

每个URL都要求用户通过认证,可以通过给http.authorizeRequests()方法添加子方法的方式为每个URL指定自定义要求。

  1. http.authorizeRequests()下添加了多个匹配器,每个匹配器用来控制不同的URL接受不同的用户访问。简单讲,http.authorizeRequests()就是在进行请求的权限配置。
  2. 所有用户都可以访问以/resources/**开头的URL,和/signup/about两个URL。
  3. 拥有ADMIN角色的用户可以访问以/admin/开头的URL。hasRole(String):如果当前用户有String表示的角色,则返回True。
  4. 同时拥有ADMINDBA角色的用户可以访问以/db/**开头的URL。access(String):当String为true时才可进行访问。
  5. 所有没被匹配器匹配到的URL都需用户通过认证。
  6. and()返回一个SecurityBuilder。Spring Security支持两种认证方式:formLogin()httpBasic()
protected void configure(HttpSecurity http) throws Exception {
	http
		.authorizeRequests()                                                            1
		.antMatchers("/resources/**", "/signup", "/about").permitAll()                  2
		.antMatchers("/admin/**").hasRole("ADMIN")                                      3
		.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")            4
		.anyRequest().authenticated()                                                   5
		.and()
		// ...
		.formLogin();
}

自定义登录页面具体实现

如果觉得他的登录界面太丑,可以自己设置登录界面

1.引入Thymeleaf引擎动态处理页面内容

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
	<groupId>org.thymeleaf.extras</groupId>
	<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>

2.编写自己的登录界面于src/main/resources/templates/login.html

  • 登录表单属性action=”/login"method="post",也就是点击请求时候向/login提交post请求;
  • 用户名输入框name="username"这个是Spring-Security约定的用户名;
  • 密码输入框name="password",这个也是Spring-Security约定的密码;
  • 表单中包含一个提交按钮。
<form action="/login" method="post">
   <div class="form-group ">
        <input type="text" name="username" class="form-control" placeholder="手机号">
    </div>
    <div class="form-group ">
        <input type="password" name="password" class="form-control" placeholder="密码">
    </div>
    <button type="submit" class="btn btn-primary btn-block btn-flat">
        登录
    </button>
</form>

3.添加控制器,请求login.html时候显示模板文件夹中的登录表单

@RestController
public class SystemController {
    @GetMapping("/login.html")
    public ModelAndView loginForm(){
        return new ModelAndView("login");
    }
}

4.配置SecurityConfig类的configure(HttpSecurity http)方法设置表单登录

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity.csrf().disable().authorizeRequests()
            .antMatchers(
                    "/index.html",
                    "/img/*",
                    "/js/*",
                    "/css/*",
                    "/bower_components/**",
                    "/login.html").permitAll()
            //任何请求需要认证authenticated
            .anyRequest().authenticated().and()//采用表单进行认证
            .formLogin()
            .loginPage("/login.html")
            .loginProcessingUrl("/login")
            .failureUrl("/login.html?error")
            .defaultSuccessUrl( "/index.html").and()
            .logout()
            .logoutUrl("/logout")
            .logoutSuccessUrl("/login.html?logout" );
}

参考资料

  • HttpSecurity初步理解:https://blog.csdn.net/yy_diego/article/details/92800756
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值