第九章、权限管理

在第八章的基础上进行开发

一、建立角色与资源的关系

二、CSRF问题的解决

三、启用方法级别的安全设置

四、使用BCrypt加密密码

五、用户登录

六、记住我

在UserServiceImpl中实现UserDetailsService接口,重写方法

//通过用户账号加载用户的认证信息
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
	return userRepository.findByUsername(username);
}

SecurityConfig类进行完善如下

package com.waylau.spring.boot.blog.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * 安全配置类
 */
@EnableWebSecurity//启用web安全
@EnableGlobalMethodSecurity(prePostEnabled = true)//启用方法安全设置
public class SecurityConfig extends WebSecurityConfigurerAdapter{

	private static final String KEY = "waylau.com";
	
	@Autowired
	private UserDetailsService userDetailsService;
	
	@Autowired
	private PasswordEncoder passwordEncoder;
	
	@Bean
	public PasswordEncoder passwordEncoder(){
		return new BCryptPasswordEncoder();//使用BCrypt加密
	}
	
	@Bean
	public AuthenticationProvider authenticationProvider(){
		DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
		authenticationProvider.setUserDetailsService(userDetailsService);
		authenticationProvider.setPasswordEncoder(passwordEncoder);//设置密码加密方式
		return authenticationProvider;
	}
	
	/**
	 * 自定义配置
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests().antMatchers("/css/**", "/js/**", "/fonts/**", "/index").permitAll()//都可以访问
			.antMatchers("/h2-console/**").permitAll()//都可以访问
			.antMatchers("/admins/**").hasRole("ADMIN")//需要相应的角色才可以可以访问
			.and()
			.formLogin()//基于Form表单登录验证
			.loginPage("/login").failureUrl("/login-error")//自定义登录界面  登录失败会重定向到/login-error
			.and().rememberMe().key(KEY)//启用remenber me
			.and().exceptionHandling().accessDeniedPage("/403");//处理异常  拒绝访问就重定向到/403
		
		http.csrf().ignoringAntMatchers("/h2-console/**");//禁用H2控制台的CSRF防护
		http.headers().frameOptions().sameOrigin();//允许来自统一来源的H2控制台请求
	}
	
	/**
	 * 认证信息的管理
	 * @param auth
	 * @throws Exception
	 */
	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
		auth.userDetailsService(userDetailsService);//认证信息存储于数据库中
		auth.authenticationProvider(authenticationProvider());
	}
}

由于配置中启用了CSRF防护,也就是说只有GET|HEAD|TRACE|OPTIONS这4类方法会被放行,其它Method的http请求(例如POST请求)都要验证_csrf的token是否正确

若没有携带该token,那么请求会被拒绝,被认为是非法的请求  Form表单会自动携带csrf的token

下面完善登录功能

在header.html的header中添加两个标签如下,该header.html会被各个页面引入使用

<!-- CSRF -->
<meta name="_csrf" th:content="${_csrf.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>

以及如下(未认证时候显示登录和注册  认证后显示个人主页、设置等)

<nav class="navbar navbar-inverse bg-inverse navbar-toggleable-md">
    <div class="container">
        <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse"
                data-target="#navbarsContainer" aria-controls="navbarsExampleContainer" aria-expanded="false"
                aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <a class="navbar-brand" href="/" th:href="@{/}">NewStarBlog</a>

        <div class="collapse navbar-collapse" id="navbarsContainer">

            <ul class="navbar-nav mr-auto">
                <li class="nav-item">
                    <a class="nav-link" href="/blogs?order=new">最新 <span class="sr-only">(current)</span></a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="/blogs?order=hot">最热</a>
                </li>
                <li>
	                <form class="form-inline mt-2 mt-md-0">
	                    <input class="form-control mr-sm-2" type="text" placeholder="搜索">
	                    <a href="/blogs?keyword=ww" class="btn btn-outline-secondary my-2 my-sm-0"><i class="fa fa-search" aria-hidden="true"></i></a>
	                </form>
                </li>
            </ul>
            
            <!-- isAuthenticated() 判断是否认证 -->
            <div sec:authorize="isAuthenticated()" class="row" >
			    <div class="dropdown">
			        <a class=" dropdown-toggle" href="/u/waylau" th:href="@{'/u/' + ${#authentication.name}}" data-toggle="dropdown"><span sec:authentication="name"></span></a>
			        <div class="dropdown-menu" >
			            <a class=" dropdown-item" href="/u/waylau" th:href="@{'/u/' + ${#authentication.name}}">个人主页</a> 
			            <a class="dropdown-item" href="/u/waylau/profile" th:href="@{'/u/' + ${#authentication.name}} + '/profile'" >个人设置</a>
			        </div>
			    </div>
			    <div>
			        <a href="/u/waylau/blogs/edit" th:href="'/u/' + ${#authentication.name} + '/blogs/edit'" class="btn btn-outline-success my-2 my-sm-0">写博客</a>
			    </div>
			
			    <form action="/logout" th:action="@{/logout}" method="post">
			        <input class="btn btn-outline-success " type="submit" value="退出">
			    </form>
			</div>
			
			<!-- 是否未认证 -->
			<div sec:authorize="isAnonymous()">
			    <a href="/login" class="btn btn-outline-success my-2 my-sm-0" type="submit">登录</a>
			    <a href="/register" class="btn btn-outline-success my-2 my-sm-0" type="submit">注册</a>
			</div>
        </div>
    </div>
</nav>

再修改启动时执行的sql(主要是修改密码 此时密码已经加密)

INSERT INTO user (id, username, password, name, email) VALUES (1, 'admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', '老卫', 'i@waylau.com');
INSERT INTO user (id, username, password, name, email) VALUES (2, 'waylau', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', 'Way Lau', 'waylau@waylau.com');

INSERT INTO authority (id, name) VALUES (1, 'ROLE_ADMIN');
INSERT INTO authority (id, name) VALUES (2, 'ROLE_USER');

INSERT INTO user_authority (user_id, authority_id) VALUES (1, 1);
INSERT INTO user_authority (user_id, authority_id) VALUES (2, 2);

启动springboot 访问地址如下  http://localhost:8080/admins   由于此时未登录,会重定向到login页面

这时使用一个普通账号登录  waylau  123456

显示页面如下

因为waylau 是ROLE_USER角色  没有/admins的访问权限

退出后使用admin账号登录

页面显示如下

此时具有访问权限

登录页面中的 记住我  

<input type="checkbox" id="remember-me" name="remember-me"><label for="remember-me">&nbsp;记住我</label>

当选中后,登录 直接关闭浏览器,再次打开浏览器,则为已登录状态

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

荒--

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值