Spring Security的使用(一)

Spring Security的使用(一)

整体流程

一个使用Spring Security编写的注册登录的Demo,分管理员和用户两种角色,登陆后可通过Spring Security控制页面的控件显隐达到区分管理员和用户的目的。

代码部分
Spring Security依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
    <!--springsecurity4 要指定3.0以上版本,否则权限标签可能无法工作-->
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    <version>3.0.4.RELEASE</version>
</dependency>
Spring Security配置类
配置授权方式

重写protected void configure(HttpSecurity http)

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable();
    http.formLogin()
            //默认登录页面
            .loginPage("/login.html")
            //和登录时的表单的action地址对应,表明该form请求由spring security处理
            .loginProcessingUrl("/authentication/form")
            //默认登录请求成功页面
            .defaultSuccessUrl("/index",true)
            //认证失败跳转页面
            .failureForwardUrl("/errorinfo")
            .and()
            .authorizeRequests()
            //放行一下请求
            .antMatchers("/login.html","/druid/**","/static/**","/register").permitAll()
            .anyRequest()//对任何请求
            .authenticated()//开启认证
            .and()
            //关闭druid可视化页面的防劫持(如果使用druid内置的可视化监控平台)
            .headers().frameOptions().disable()
            .and()
            //开放Spring Security的“/logout”注销
            .logout().permitAll();//开放注销访问
}
配置登录认证

重写protected void configure(AuthenticationManagerBuilder auth)

//登录认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(LoginUserCheckUtil());
}
将Spring Security默认的加密方式进行注入
//spring security提供的BCrypt加密方式
@Bean
public BCryptPasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
将具体执行登录认证的过程注入到配置类中
//将登录认证工具类注入到Bean
@Bean
UserDetailsService LoginUserCheckUtil(){
    return new LoginUserCheckUtil();
}
进行登录认证的工具类

利用@Component将本工具类变为一个Spring的组件,因为项目持久层使用的是mybatis,所以将Mapper注入到工具类中方便执行sql对数据库进行操作

@Component
public class LoginUserCheckUtil implements UserDetailsService {

    @Autowired
    private LoginMapper loginMapper;

    //参数username就是在页面登录时输入的用户名,会自动传到此方法中
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //执行sql以传入的用户名为条件在数据库进行查询
        T_User t_user = loginMapper.selectuserByUsername(username);
        //声明List存储该角色的权限集合
        ArrayList<GrantedAuthority> grantedAuthority = new ArrayList<>();
        //利用用户名获得其角色再查询数据库获得该角色的权限
        String authorities = loginMapper.selectRoleByUserRoleid(t_user.getRole_id());
        //对角色进行判断,并赋予其对应的权限
        //比如它有个表达式hasRole("ADMIN")。那它实际上查询的是用户权限集合中是否存在字符串"ROLE_ADMIN"。如果你从角色表中取出用户所拥有的角色时不加上"ROLE_"前缀,那验证的时候就匹配不上了。
        if(authorities.equals("admin")){
            authorities="ROLE_ADMIN";
        }else if (authorities.equals("user")){
            authorities="ROLE_USER";
        }
        //用获得的权限进行初始化
        GrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(authorities);
        //添加到权限集合中
        grantedAuthority.add(simpleGrantedAuthority);
        return new User(t_user.getUsername(),t_user.getPassword(),true,true,true,true,grantedAuthority);

    }
}

返回一个User对象,这个User时Spring Security内置的对象,主要使用用户名,密码,权限三个属性。

也可以自定义对象需要实现UserDetails。

至此登录认证过程结束,认证成功会返回一个带有用户名,密码,权限的User对象;否则认证失败,会跳转到授权时 .failureForwardUrl指向的URL。

在注册时别忘记也是用BCryptPasswordEncoder方法进行加密,否则对比不成功(当然也可使用自定义的加密算法)。

页面中对控件的权限控制

sec:authorize=“hasRole(‘ROLE_USER’)”

sec:authorize=“hasRole(‘ROLE_ADMIN’)”

利用上面的属性进行判断然后控制页面控件的显隐,对不同权限的用户开放不同的页面功能

<li id="yuangong" class="layui-nav-item" sec:authorize="hasRole('ROLE_USER')">
    <a href="javascript:;">用户功能
        <span class="layui-nav-more"></span>
    </a>
    <dl class="layui-nav-child">
        <dd><a href="Leavepage" target="info"><span>我要请假</span></a></dd>
        <dd><a href="worktext" target="info"><span>工作日志</span></a></dd>
        <dd><a href="History" target="info"><span>历史公告</span></a></dd>
    </dl>
</li>
<li id="zhineng" class="layui-nav-item" sec:authorize="hasRole('ROLE_ADMIN')">
    <a href="javascript:;">管理员功能
        <span class="layui-nav-more"></span>
    </a>
    <dl class="layui-nav-child">
        <dd><a href="managerinfo" target="info"><span>员工档案</span></a></dd>
        <dd><a href="Leavemanage" target="info"><span>请假管理</span></a></dd>
        <dd><a href="worktextmanager" target="info"><span>日志管理</span></a></dd>
        <dd><a href="announcement" target="info"><span>公告发布</span></a></dd>
        <dd><a href="History" target="info"><span>历史公告</span></a></dd>
    </dl>
</li>
BCryptPasswordEncoder加密算法

BCryptPasswordEncoder算法内部使用的加随机盐(salt)的方式保证安全性,以往的MD5加密方式对相同字符串加密出的密文相同,存在一定的风险;但现在通过随机盐的方式保证了密文的安全性。

加随机盐

首先得到算法的版本、强度和随机安全实例

public BCryptPasswordEncoder(BCryptVersion version, int strength, SecureRandom random) {
    if (strength != -1 && (strength < BCrypt.MIN_LOG_ROUNDS || strength > BCrypt.MAX_LOG_ROUNDS)) {
        throw new IllegalArgumentException("Bad strength");
    }
    this.version = version;
    this.strength = strength == -1 ? 10 : strength;
    this.random = random;
}

生成随机盐

参数:真实密码

public String encode(CharSequence rawPassword) {
    String salt;
    if (random != null) {
        salt = BCrypt.gensalt(version.getVersion(), strength, random);
    } else {
        salt = BCrypt.gensalt(version.getVersion(), strength);
    }
    return BCrypt.hashpw(rawPassword.toString(), salt);
}

matches方法用来校验登录密码和数据库存储的密码是否一致

参数:真实密码,密码的密文

首先校验密码是否存在

public boolean matches(CharSequence rawPassword, String encodedPassword) {
    if (encodedPassword == null || encodedPassword.length() == 0) {
        logger.warn("Empty encoded password");
        return false;
    }

    if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
        logger.warn("Encoded password does not look like BCrypt");
        return false;
    }

    return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}

将纯文本密码和hash后的密码传入

public static boolean checkpw(String plaintext, String hashed) {
    return equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed));
}

利用StandardCharsets编码常量,将字符串密码转换为字符数组进行比较

static boolean equalsNoEarlyReturn(String a, String b) {
    return MessageDigest.isEqual(a.getBytes(StandardCharsets.UTF_8), b.getBytes(StandardCharsets.UTF_8));
}

关于权限中的defaultsuccessurl和forsuccessurl的区别参考:

https://my.oschina.net/lenglingx/blog/4311577

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值