Spring Security 简单认证与授权

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC、DI和AOP功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

Spring Security 是一个当今非常流行的安全框架。

环境

Spring Boot2.3.0.RELEASE
Spring Security5.3.2
IntelliJ IDEA 2020.1 x64

内存用户认证与授权

首先创建一个 SpringBoot 项目,引入 Spring Security 的依赖,在没有写 Spring Security 的相关配置时,启动项目。访问自己写的一个页面,会发现也是需要进行登录的,虽然自己并没有用户密码的验证,这时 Spring Security 默认提供了一个 user 账号,密码在启动时随机生成,登录之后才能访问到自己写的页面。
1)引入 Spring Security 依赖

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

2)写一些 html 页面(如:主页、自定义登录页、其他一些功能页)
在这里插入图片描述
3)Controller 处理请求

@Controller
public class MainController {
    @RequestMapping("/index")
    public String index(){
        return "index";
    }

    @RequestMapping("/login")
    public String mainPage(){
        System.out.println("/login....");
        return "normal/success";
    }

    @RequestMapping("/fail")
    public String failPage(){
        return "fail";
    }

    @RequestMapping("/add")
    public String addPage(){
        return "normal/resadd";
    }
	....
}

4)配置认证与授权规则

@Configuration
@EnableWebSecurity
public class SpringsecurityConfig extends WebSecurityConfigurerAdapter {
    //@Autowired
    //private SysUserService userService;

    //配置加密
    //@Bean  //这里注释掉不启用加密
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(); //spring security 内置加密算法
    }

    //认证用户的来源
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        //auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
		//下面定义了两个内存用户
        auth.inMemoryAuthentication().withUser("xiao")
                                    .password("{noop}123")  //noop 表示没有加密
                                    .roles("NORMAL"); //用户xiao有NORMAL角色

        auth.inMemoryAuthentication().withUser("admin")
                                    .password("{noop}123")
                                    .roles("NORMAL") //用户admin有NORMAL角色
                                    .authorities("add","del");  //用户admin有"add"和"del"的权限


    }

    //Spring Security配置
    public void configure(HttpSecurity hs) throws Exception {
        hs.authorizeRequests()
                .antMatchers("/index","/css/**").permitAll() //允许所有请求访问
                .antMatchers("/add","/del").hasAnyAuthority("add","del") //  /add 和/del 需要 add或del的权限
                .antMatchers("/**").hasAnyRole("NORMAL")  //所有资源都要 NORMAL 这个角色才能访问
                .anyRequest().authenticated() //其他请求只需要认证即可访问
                .and()  //开始新的配置
                .formLogin() //以表单形式登录
                .loginPage("/index")  //使用自定义登录页面
                .loginProcessingUrl("/login")
                .successForwardUrl("/login") //登录成功跳转的页面
                .failureForwardUrl("/fail") //登录失败跳转页面,其实可以不写,默认回到登录页

                .and()
                .csrf()
                .disable(); //不控制跨站攻击,Security 默认对跨站攻击做了安全控制
    }
}

5)测试
启动项目,直接访问 /login、/add 等页面是会默认跳转回 /index 到登录页面的
使用 xiao/123 或 admin/123 登录成功,可以访问有权访问的页面;否则到了 fail.html 页面


用不正确的账号密码

这个例子中,我发现登录之后,访问了一个功能页,发现点网页的后退<–,会出错,提示 “确认重新提交表单”,没弄明白是哪里出问题了。如果有朋友知道,希望留言指点一下,先谢谢了

自定义错误页面

当上面的例子中用 xiao 账号登录时,想访问增加的页面,但是没有权限,会看到一个默认提共的错误页面,对用户很不友好。所以应该自定义一个普通用户能看懂的页面

写一个配置类

@Configuration
public class ErrorPageConfig implements ErrorPageRegistrar {
    @Override
    public void registerErrorPages(ErrorPageRegistry registry) {
        ErrorPage errorPage = new ErrorPage(HttpStatus.FORBIDDEN, "/403");
        registry.addErrorPages(errorPage);
    }
}

然后当然需要在 controller 类中加入对应的请求

    @RequestMapping("/403") //对应 ErrorPageConfig中的路径
    public String forbidden() {
        return "403";
    }

这样,当用户访问没有权限的页面时就能看到我们自定义的错误页面了。当然,还可以定义其他的错误页面,具体看看 HttpStatus 类中的状态码。
在这里插入图片描述
在这里插入图片描述

数据库用户认证与授权

准备数据库(本文用到mysql8.0.20)

本文配置了所有资源都要 NORMAL 角色才能访问,数据库的角色名要写成为 ROLE_NORMAL,否则角色名不正确,登录成功了也无法访问其他功能

1)加入依赖

       <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

2)加入相关配置

## 配置数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/demo_database?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=yourpwd

## mybatis配置
mybatis.type-aliases-package=com.xiao.springsecurity.entity
mybatis.configuration.map-underscore-to-camel-case=true

logging.level.com.xiao.springsecurity=debug

3)编写实体类
需要实现相应的 Spring Security 接口,记得把重写方法的布尔值都改为 true

public class SysUser implements UserDetails {
    private Integer id;
    private String username;
    private String password;

    private List<SysRole> roles;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return roles;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }
    .....
}
public class SysRole implements GrantedAuthority {
    private Integer id;
    private String roleName;
    private String roleDesc; //角色描述

    @JsonIgnore  //该注解分布式实现需要
    @Override
    public String getAuthority() {
        return roleName;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }
    .....
}

4)编写mapper

public interface SysUserMapper extends Mapper {
    @Select("select * from sys_user where username = #{username}")
    @Results({
            @Result(id=true, property = "id", column = "id"),
            @Result(property = "roles", column = "id", javaType = List.class,
            many = @Many(select = "com.xiao.springsecurity.mapper.SysRoleMapper.findRolesByUid"))
    })
    SysUser findUserByName(String username);
}

角色表字段与角色类属性不一致,使用别名

public interface SysRoleMapper extends Mapper {
    @Select("select sr.id, sr.role_name roleName, sr.role_desc roleDesc " +
            "from sys_user_role sur, sys_role sr " +
            "where sur.rid=sr.id and sur.uid = #{uid}")
    List<SysRole> findRolesByUid(Integer uid);
}

5)编写service

接口继承 UserDetailsService ,要不然 Spring Security 无法识别

public interface SysUserService extends UserDetailsService {
}
@Service
public class SysUserServiceImpl implements SysUserService {
    @Autowired(required = false)
    private SysUserMapper userMapper;
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("123密文:" + passwordEncoder.encode("123"));
        SysUser sysUser = userMapper.findUserByName(username);
        System.out.println(sysUser.getUsername() + "====" + sysUser.getPassword());
        List<SysRole> roles= (List<SysRole>) sysUser.getAuthorities();
        for (int i = 0; i < roles.size(); i++) {
            System.out.println("数据库角色名称:" + roles.get(i).getRoleName());
        }

        /* 用下面这个返回 user 也可以,不过数据不是从数据库取的了
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("NORMAL"));
        User user = new User(username,passwordEncoder.encode("123"),authorities);

         */
        return sysUser;
    }
}

6)修改 Spring Security 配置类

@Configuration
@EnableWebSecurity
public class SpringsecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private SysUserService userService;

    //配置加密
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(); //spring security 内置加密算法
    }

    //认证用户的来源
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
/*
        auth.inMemoryAuthentication().withUser("xiao")
                                    .password("{noop}123")  //noop 表示没有加密
                                    .roles("NORMAL");

        auth.inMemoryAuthentication().withUser("admin")
                                    .password("{noop}123")
                                    .roles("NORMAL")
                                    .authorities("add","del");
*/

    }

    //Spring Security配置
    public void configure(HttpSecurity hs) throws Exception {
        hs.authorizeRequests()
                .antMatchers("/index","/css/**").permitAll()
                //.antMatchers("/add","/del").hasAnyAuthority("add","del")
                .antMatchers("/**").hasAnyRole("NORMAL")
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/index")
                .loginProcessingUrl("/login")
                .successForwardUrl("/login")
                .failureForwardUrl("/fail")

                .and()
                .csrf()
                .disable();
    }
}

7)测试
记得在启动类加上 Mapper 扫描注解

@SpringBootApplication
@MapperScan("com.xiao.springsecurity.mapper")
public class SpringsecurityApplication {
...

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值