project(1)

12 篇文章 0 订阅
6 篇文章 0 订阅

Spring 安全框架

什么是Spring安全框架

Spring-Security(Spring安全框架)是Spring提供的安全管理组件

是Spring框架环境下提供的安全管理和权限管理的组件

一个项目一般都会有登录功能,我们之前编写的登录功能非常简陋,不能用于实际开发

Spring-Security提供了专业的实现登录的方式,供我们使用

使用Spring-Security实现登录

基本使用

步骤1:

导入依赖

<!-- Spring Security -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Spring Security Test -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <scope>test</scope>
</dependency>

步骤2:启动服务

在启动服务的控制台信息中会包含一个随机产生的密码

用于登录Spring-Security框架

用默认的用户名是"user"

密码赋值控制台出现的,粘贴登录即可

步骤3:

我们不想用它提供的用户名和密码,就可以自己设置

application.properties文件中添加配置如下

# Spring-Security配置用户名密码
spring.security.user.name=admin
spring.security.user.password=123456

步骤4:

密码加密

实际开发中,一定不可能在任何位置保存明文密码

将用户的密码经过加密之后再保存,能够最大限度的提高密码的安全级别

而加密的过程不需要我们编写,使用现成的加密规则即可

我们使用BCrypt的加密规则

首先我们新建一个包,cn.tedu.straw.portal.security

配置类SecurityConfig,在这个类中注入加密对象

代码如下

//@Configuration表示当前类是配置类,可能向Spring容器中注入对象
@Configuration
public class SecurityConfig {

    //注入一个加密对象
    @Bean
    public PasswordEncoder passwordEncoder(){
        //这个加密对象使用BCrypt加密内容
        return new BCryptPasswordEncoder();
    }
}

步骤5:

下面进行测试,测试加密功能和验证功能

代码如下

@SpringBootTest
public class SecurityTest {

    @Autowired
    PasswordEncoder passwordEncoder;

    @Test
    public void encodeTest(){
        /*
            每次运行加密结果不同
            是因为加密对象采用了"随机加盐"技术,提高安全性
         */
        String pwd=passwordEncoder.encode("123456");
        System.out.println(pwd);
//$2a$10$IHMiKBqpiPFYgRg4P0E0HeU.xdkr1nw0/y1AWKIvHh5TMNwxVuBRW
    }
    @Test
    public void matchTest(){
        /*
        验证我们输入的密码是不是能匹配生成的密文
         */
        boolean b=passwordEncoder.matches("123456",
                "$2a$10$IHMiKBqpiPFYgRg4P0E0" +
                        "HeU.xdkr1nw0/y1AWKIvHh5TMNwxVuBRW");
        System.out.println(b);
    }
}

步骤6:

修改application.properties文件中配置的密码

# Spring-Security配置用户名密码
spring.security.user.name=admin
spring.security.user.password=$2a$10$IHMiKBqpiPFYgRg4P0E0HeU.xdkr1nw0/y1AWKIvHh5TMNwxVuBRW

测试登录…

步骤7:

上面的操作可以简化

将一个Spring内置的算法标记标注在application.properties文件的密文密码前

代码如下

# Spring-Security配置用户名密码
spring.security.user.name=admin
spring.security.user.password={bcrypt}$2a$10$IHMiKBqpiPFYgRg4P0E0HeU.xdkr1nw0/y1AWKIvHh5TMNwxVuBRW

安照上面的配置,我们刚刚注入的PasswordEncoder对象就可以省略了

//注入一个加密对象(密码中设定了算法ID下面的注入就省略了!)
    /*@Bean
    public PasswordEncoder passwordEncoder(){
        //这个加密对象使用BCrypt加密内容
        return new BCryptPasswordEncoder();
    }*/

权限管理

有些网页必须登录之后才能访问

即使都是登录用户,它们也有可能有身份的区别,不同身份有不同功能

只有学生才能访问发布问题的页面,

只有老师才能访问回答问题的页面

Spring-Security也方便我们来管理这些问题

下面我们就使用Spring-Security权限管理功能访问用户信息

步骤1:开启权限管理功能

SecurityConfig这类中编写代码

//@Configuration表示当前类是配置类,可能向Spring容器中注入对象
@Configuration
//下面的注解表示通知Spring-Security开启权限管理功能
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends
        WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
        //方法中配置哪个用户可以有什么样的权限
        auth.inMemoryAuthentication().withUser("Tom")
                .password("{bcrypt}$2a$10$IHMiKBqpiPFYgRg4P0E0HeU.xdkr1nw0/y1AWKIvHh5TMNwxVuBRW")
                .authorities("/tag/get");
    }
}

步骤2:

在Controller类的方法上声明访问这个方法需要什么权限

如果访问这个方法需要"/tag/get"权限,那么没有这个权限的人就不能访问

如果Controller类的方法上没有声明权限,那么就是登录的用户都可以访问

在TagController类中编写一些权限

代码如下

@RestController
//下面的注解表示想访问本控制器中的任何方法需要前缀/portal/tag
@RequestMapping("/portal/tag")
public class TagController {

    @Autowired
    private IUserService userService;
    @Autowired(required = false)
    private ITagService tagService;

    @GetMapping("/get")//localhost/portal/tag/get?id=3
    @PreAuthorize("hasAuthority('/tag/get')")
    public User get(Integer id){
        return userService.getById(3);
    }
    // 编写一套可以按tagid查询一个tag信息的控制器方法
    //  将tag信息显示在浏览器
    @GetMapping("/gettag")
    @PreAuthorize("hasAuthority('/tag/byid')")
    public Tag getTag(Integer id){
        return tagService.getById(id);
    }
}

测试运行

tom正常访问/get?id=3

访问/gettag?id=14时发生403错误,表示权限不足

现在Tom用户不能访问的localhost/portal/tag/gettag路径,因为他没有权限

我们可以通过配置将这个权限赋予Tom用户

方法如下

@Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
        //方法中配置哪个用户可以有什么样的权限
        auth.inMemoryAuthentication().withUser("Tom")
                .password("{bcrypt}$2a$10$IHMiKBqpiPFYgRg4P0E0HeU.xdkr1nw0/y1AWKIvHh5TMNwxVuBRW")
                .authorities("/tag/get","/tag/byid");
    }

使用UserDetailsService提供认证数据

我们service包中编写一个类UserDetailsServiceImpl

代码如下

/**
 * 这个类实现UserDetailsService接口
 * 为Spring-Security提供认证的数据
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    //Spring-Security认证信息时
    //会将用户名传递到这个方法中
    //根据这个用户名获得数据库中加密的密码,
    //如果匹配则登录成功
    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
       //这个方法返回的是用户的详细信息
        UserDetails user=null;
        //暂时规定正确的用户名和密码是"Jerry",加密的"123456"
        if("Jerry".equals(username)){
            //如果用户名正确,将加密的密码保存到User对象
            //下面的User类也是Spring提供的不是我们的实体类
            user= User.builder()
                    .username("Jerry")
                    .password("{bcrypt}$2a$10$IHMiKBqpiPFYgRg4P0E0HeU.xdkr1nw0/y1AWKIvHh5TMNwxVuBRW")
                    //这里也能直接赋予权限
                    .authorities("/tag/get")
                    .build();
        }
        System.out.println("user:"+user);
        return user;
    }
}

回到SecurityConfig类中,删除configer方法之前的代码

将方法改写为:

//@Configuration表示当前类是配置类,可能向Spring容器中注入对象
@Configuration
//下面的注解表示通知Spring-Security开启权限管理功能
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends
        WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailsServiceImpl userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.userDetailsService(userDetailsService);
    }
}

关于用户的权限

数据库中有:

permission表,保存权限

role表,保存角色

role_permission表,保存角色和权限的关系

role是permission多对多关系,多对多关系的表一定会出现一张中间表,来保存他们的关系

user表,保存用户信息

user_role表,保存用户和角色的关系

user和role表也是多对多的关系

我们在登录用户时需要指定用户的权限,根据用户的id查询权限可能需要使用这5张表的连接查询

除了对权限的查询外,还需要用户的基本信息,使用用户名查询出用户对象即可

在UserMapper接口中添加如下两个查询

@Repository
public interface UserMapper extends BaseMapper<User> {

    //根据用户输入的用户名查询用户信息的方法
    @Select("select * from user where username=#{username}")
    User findUserByUsername(String username);

    //查询指定id的用户的所有权限
    @Select("SELECT p.id,p.name" +
            " FROM user u" +
            " LEFT JOIN user_role ur ON u.id=ur.user_id" +
            " LEFT JOIN role r ON r.id=ur.role_id" +
            " LEFT JOIN role_permission rp ON r.id=rp.role_id" +
            " LEFT JOIN permission p ON p.id=rp.permission_id" +
            " WHERE u.id=#{id}")
    List<Permission> findUserPermissionsById(Integer id);

}

最好还是测试一下,保证万无一失!

测试代码如下

	@Autowired
    UserMapper userMapper;
    @Test
    public void findUser(){
        User user=userMapper.findUserByUsername("tc2");
        System.out.println(user);
    }
    @Test
    public void findPermissions(){
        List<Permission> list=userMapper.findUserPermissionsById(3);
        for(Permission p: list){
            System.out.println(p);
        }
    }

测试成功!!!

连接数据库实现Spring-Security登录

步骤1:

在编写IUserService接口中添加一个获得用户详情的方法

public interface IUserService extends IService<User> {

    //这个方法用法查询获得用户详情对象的业务
    //UserDetails是SpringSecurity验证用户必要的信息
    //String username是SpringSecurity接收的用户输入的用户名
    UserDetails getUserDetails(String username);
}

步骤2:

在impl包下的UserServiceImpl类中实现这个方法

@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) {
            //如果为空直接返回null
            return null;
        }
        //如果不为空根据用户的id查询这个用户的所有权限
        List<Permission> permissions=
                userMapper.findUserPermissionsById(user.getId());
        //将权限List中的权限转成数组方便赋值
        String[] auths=new String[permissions.size()];
        for(int i=0;i<auths.length;i++){
            auths[i]=permissions.get(i).getName();
        }
        //创建UserDetails对象,并为他赋值
        UserDetails ud= org.springframework.security.core.userdetails
                .User.builder()
                .username(user.getUsername())
                .password(user.getPassword())
                .accountLocked(user.getLocked()==1)//写==1是判断锁定
                .disabled(user.getEnabled()==0)//写==0是判断不可用
                .authorities(auths).build();
        //最后返回UserDetails对象
        return ud;
    }
}

步骤3:测试代码

   @Autowired
    IUserService userService;
    @Test
    public void abcLogin(){
        UserDetails ud=userService.getUserDetails("tc2");
        System.out.println(ud);
    }

步骤4:

UserDetailsServiceImpl类中来调用刚刚编写的UserServiceImpl类中的方法

返回UserDetails对象即可

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    //Spring-Security认证信息时
    //会将用户名传递到这个方法中
    //根据这个用户名获得数据库中加密的密码,
    //如果匹配则登录成功
    @Autowired
    IUserService userService;

    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        return userService.getUserDetails(username);
    }
}

在这里插入图片描述

控制授权范围

网站有些页面需要登录后才能访问,但是有些直接就可以访问

我们设置一下授权范围,无论是否登录都可以访问首页

代码如下

//@Configuration表示当前类是配置类,可能向Spring容器中注入对象
@Configuration
//下面的注解表示通知Spring-Security开启权限管理功能
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends
        WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailsServiceImpl userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.userDetailsService(userDetailsService);
    }
    
	//控制授权代码在这里!!!!!
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()//对当前全部请求进行授权
            .antMatchers(
                    "/index.html",
                    "/img/*",
                    "/js/*",
                    "/css/*",
                    "/bower_components/**"
            )//设置路径
            .permitAll()//允许全部请求访问上面定义的路径
            //其它路径需要全部进行表单登录验证
            .anyRequest().authenticated().and().formLogin();

    }

随笔

123456

234567

ACDCB

BDADC

开发顺序: mapper->service->controller->html

逆向开发:html->controller->service->mapper

以后编写Service一般都是先在对应Service的接口中添加方法

再找到对应接口的实现类编写代码

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值