SpringBoot---SpringSecurity(自定义登录页面+Csrf-Token配置)

SpringSecurity

1、SpringSecurity主要还是在配置文件里面配置

  • 类前面添加注解 @EnableWebSecurity
  • 继承 WebSecurityConfigurerAdapter
  • 注入自带的DataSource
  • 注入重写的UserDetailsService
  • 重写用户身份认证方法 configure(AuthenticationManagerBuilder auth)
  • 重写用户授权方法 configure(HttpSecurity http)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    DataSource dataSource; //自带的
@Resource UserDetailsService userDetailsService; //需要自己重写
//实现用户身份认证 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); auth.userDetailsService(userDetailsService).passwordEncoder(encoder); } @Override protected void configure(HttpSecurity http) throws Exception { //配置url的访问权限 http.authorizeRequests() .antMatchers("/").permitAll() .antMatchers("/**update**").permitAll() .antMatchers("/login/**").permitAll() .antMatchers("/detail/common/**").hasRole("common") //common权限控制 .antMatchers("/detail/vip/**").hasRole("vip") //vip权限控制 .anyRequest().authenticated(); //关闭csrf保护功能 http.csrf().disable(); //使用自定义的登录窗口 http.formLogin() .loginPage("/userLogin").permitAll() //userLogin对应控制器中的自定义登录路径 .usernameParameter("username").passwordParameter("password") //username和password对应前端表单的name键 .defaultSuccessUrl("/") //登录成功后跳转 .failureUrl("/userLogin?error"); //登录失败跳转
//实现注销 (会清空session) http.logout() .logoutUrl("/mylogout") //只能是post方法,/mylogout是前端的请求 .logoutSuccessUrl("/userLogin"); //记住我 http.rememberMe() .rememberMeParameter("rememberme").tokenValiditySeconds(200) //200秒 .tokenRepository(tokenRepository()); //配置持久化token } //持久化token存储
  //数据库的表必须是persistent_logins ,字段必须是username、series(序列号)、token、last_used(更新时间)
  @Bean public JdbcTokenRepositoryImpl tokenRepository(){ JdbcTokenRepositoryImpl jr=new JdbcTokenRepositoryImpl(); jr.setDataSource(dataSource); return jr; } }

2、登录跳转控制器

@Controller
public class LoginController {
    @GetMapping("/userLogin")
    public String login(){
        return "login/login";}}

3、重写 loadUserByUsername 方法

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    TCustomerService customerService;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //获取用户对象
        TCustomer customer = customerService.getByUsername(s);
        if (customer != null) {
            List<TAuthority> authorities = customerService.listAuthorityByUsername(s);

            //对用户权限进行转换
            List<SimpleGrantedAuthority> list = authorities.stream()
                    .map(authority -> new SimpleGrantedAuthority(authority.getAuthority()))
                    .collect(Collectors.toList());
//            System.out.println("封装userDetails");
            UserDetails userDetails = new User(customer.getUsername(), customer.getPassword()
                    , list);
            return userDetails;
        } else {
            System.out.println("用户名不存在");
            throw new UsernameNotFoundException("当前用户不存在");
        }
    }
}

4、登录页面login.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>用户登录界面</title>
    <link th:href="@{/login/css/bootstrap.min.css}" rel="stylesheet">
    <link th:href="@{/login/css/signin.css}" rel="stylesheet">
</head>
<body class="text-center">
    <form class="form-signin" th:method="post" th:action="@{/userLogin}">
        <img class="mb-4" th:src="@{/login/img/login.jpg}" width="72px" height="72px">
        <h1 class="h3 mb-3 font-weight-normal">请登录</h1>
        <!-- 用户登录错误信息提示框 -->
        <div th:if="${param.error}" style="color:red;height:40px;text-align:left;font-size:1.1em">
            <img th:src="@{/login/img/loginError.jpg}" width="20px">用户名或密码错误,请重新登录!
        </div>
      <!--username和password要和哦欸之文件的key一 一对应-->
     <input type="text" class="form-control" placeholder="用户名" required="" autofocus="" name="username"> <input type="password" class="form-control" placeholder="密码" required="" name="password"> <div class="checkbox mb-3"> <label> <input type="checkbox" name="rememberme"> 记住我 <!--rememberme要与配置文件的key一 一对应--> </label> </div> <button class="btn btn-lg btn-primary btn-block" type="submit" >登录</button> <p class="mt-5 mb-3 text-muted">Copyright© 2019-2020</p> </form> </body> </html>

5、注销前端 (th:action="@{/mylogout}" 的mylogout必须与配置的路径对应,且请求方法为post)

<form th:action="@{/mylogout}" method="post">
    <input th:type="submit" th:value="注销" />
</form>

5、常用标签

(1)sec:authorize="isAuthenticated()"

判断用户是否已经登陆认证,引号内的参数必须是isAuthenticated()。

(2)sec:authentication=“name”

获得当前用户的用户名,引号内的参数必须是name。

(3)sec:authorize=“hasRole(‘role’)”

判断当前用户是否拥有指定的权限。引号内的参数为权限的名称。

(4)sec:authentication="principal.authorities"

获得当前用户的全部角色,引号内的参数必须是principal.authorities。


<html xmlns="http://www.w3.org/1999/xhtml" 
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">

<
body> <h1 align="center">欢迎进入电影网站首页</h1> <div sec:authorize="isAnonymous()"> <h2 align="center">游客您好,如果想查看电影<a th:href="@{/userLogin}">请登录</a></h2> </div> <div sec:authorize="isAuthenticated()"> <h2 align="center"><span sec:authentication="name" style="color: #007bff"></span>您好,您的用户权限为 <span sec:authentication="principal.authorities" style="color:darkkhaki"></span>,您有权观看以下电影</h2> <form th:action="@{/mylogout}" method="post"> <input th:type="submit" th:value="注销" /> </form> </div> <hr> <div sec:authorize="hasRole('common')"> <h3>普通电影</h3> <ul> <li><a th:href="@{/detail/common/1}">飞驰人生</a></li> <li><a th:href="@{/detail/common/2}">夏洛特烦恼</a></li> </ul> </div> <div sec:authorize="hasAuthority('ROLE_vip')"> <h3>VIP专享</h3> <ul> <li><a th:href="@{/detail/vip/1}">速度与激情</a></li> <li><a th:href="@{/detail/vip/2}">猩球崛起</a></li> </ul> </div> </body>

 6、结合security使用csrf-token前端

 <form  method="post" action="updateUser">  //针对表单数据修改的CSRF Token配置
        <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
        用户名: <input type="text" name="username" /><br />&nbsp;&nbsp;码: <input type="password" name="password" /><br />
        <button type="submit">修改</button>
</form>
<head> //针对ajax数据修改的CSRF Token配置
    <meta name="_csrf" th:content="${_csrf.token}"/>
    <meta name="_csrf_header" th:content="${_csrf.headerName}"/>
</head>

$(function () {
    var token = $("meta[name='_csrf']").attr("content");
    var header = $("meta[name='_csrf_header']").attr("content");
    $(document).ajaxSend(function(e, xhr, options) {
        xhr.setRequestHeader(header, token);
    });
});

后台获取数据

   @ResponseBody
    @PostMapping("/updateUser")
    public String updateUser(String username,String password,HttpServletRequest request){
         System.out.println("username:"+username);
         System.out.println("password:"+password);
         String csrf_token=request.getParameter("_csrf");
         System.out.println("csrf_token:"+csrf_token);
         return "OK";
    }

csrf讲解:https://blog.csdn.net/xiaoxinshuaiga/article/details/80766369

源码

链接:https://pan.baidu.com/s/1qzmrZCe315l87GeMrAAP5Q
提取码:yuwt 

 

posted @ 2020-09-11 11:06  codeing123  阅读(4401)  评论(0)    收藏  举报