前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringSecurity笔记之helloworld

SpringSecurity笔记之helloworld

作者头像
CBeann
发布2023-12-25 17:16:28
1210
发布2023-12-25 17:16:28
举报
文章被收录于专栏:CBeann的博客CBeann的博客

构建项目

前提

会SpringBoot和tymeleaf

目的

了解SpringSecurity的helloworld

感悟

(1)核心就是继承WebSecurityConfigurerAdapter实现类里的configure(HttpSecurity http) 方法

(2)handler和Filter是加功能的关键

初始化项目SpringBoot+web+security

pom
代码语言:javascript
复制
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springsecuritydemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springsecuritydemo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
测试
代码语言:javascript
复制
    @RequestMapping("/hello")
    @ResponseBody
    public String hello() {
        return "hello security";
    }

项目启动后在浏览器输入 localhost:8080 (会默认有一个登陆页面)

用户名默认为 user

密码在控制台中

修改上面默认的用户名和密码

代码语言:javascript
复制
spring.security.user.name=root
spring.security.user.password=123456

默认就是下面的配置

代码语言:javascript
复制
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.formLogin()//表单登陆
                //http.httpBasic()//弹出框登陆
                .and()
                .authorizeRequests()//下面是请求配置
                .anyRequest()//任何请求
                .authenticated();//都要认证


    }
}

完善案例之数据库数据登陆(假数据)(模仿)

创建User实体类

1)实现UserDetails接口,注意完善里面的方法

2)注意,每一个了方法的含义以及返回值

代码语言:javascript
复制
package com.example.springsecuritydemo.entity;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author CBeann
 * @create 2019-08-16 10:22
 */
public class User implements UserDetails {

    private String username;
    private String password;

    private List<String> permissions = new ArrayList<>();

    /**
     * 是否被冻结、账号锁定等等标志位
     */
    private int sign = 1;

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", permissions=" + permissions +
                ", sign=" + sign +
                '}';
    }

    public List<String> getPermissions() {
        return permissions;
    }

    public void setPermissions(List<String> permissions) {
        this.permissions = permissions;
    }

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

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

    public int getSign() {
        return sign;
    }

    public void setSign(int sign) {
        this.sign = sign;
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }


    /**
     * 下边是接口的方法
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {

        StringBuffer stringBuffer = new StringBuffer("");
        for (String permission : permissions) {
            stringBuffer.append(permission + ",");
        }


        //permissions的格式为 damin,user,root(中间用逗号分开)
        String permissions = stringBuffer.substring(stringBuffer.length() - 1).toString();


        return AuthorityUtils.commaSeparatedStringToAuthorityList(permissions);
    }

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

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    //账号是否过期,true没有过期
    public boolean isAccountNonExpired() {
        //你的业务逻辑
        return true;
    }

    @Override
    //账号是否被锁定或者冻结,true为没有被冻结
    public boolean isAccountNonLocked() {
        //你的业务逻辑
        return true;
    }

    @Override
    //密码是否过期,true没有过期
    public boolean isCredentialsNonExpired() {
        //你的业务逻辑
        return true;
    }

    @Override
    //账号是否可用或者删除,true为没有被删除
    public boolean isEnabled() {
        //你的业务逻辑
        return true;
    }
}
实现自定义密码加密器(本文没有做任何加密)

(1)实现PasswordEncoder接口,实现encode加密算法和macthes比较算法(此处没有加密,直接返回明文)

(2)将自定义加密算法注入到容器中

代码语言:javascript
复制
package com.example.springsecuritydemo.config;

import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author CBeann
 * @create 2019-08-16 15:57
 */
public class MyPasswordEncoder implements PasswordEncoder {
    @Override
    public String encode(CharSequence charSequence) {//加密算法,此处没有加密
        String str = charSequence.toString();
        return str;
    }

    @Override
    public boolean matches(CharSequence charSequence, String s) {

        String target = charSequence.toString();
        if (target != null && s != null && target.equals(s)) {
            System.out.println("登陆成功");
            return true;
        }
        System.out.println("登陆失败");
        return false;
    }
}
代码语言:javascript
复制
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new MyPasswordEncoder();
    }
dao层(查询数据库)(此处用的静态数据)
代码语言:javascript
复制
package com.example.springsecuritydemo.dao;

import com.example.springsecuritydemo.entity.User;
import org.springframework.stereotype.Repository;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @author CBeann
 * @create 2019-08-16 10:21
 */
@Repository
public class UserDao {

    public static Map<String, User> users = new HashMap<>();

    static {
        //初始化数据,假装自己查数据库
        User user = new User("zhangsan", "123456");
        user.setPermissions(Arrays.asList("admin", "user"));
        User user2 = new User("lisi", "123456");
        user2.setPermissions(Arrays.asList("user"));
        users.put(user.getUsername(), user);
        users.put(user2.getUsername(), user2);


    }


    public User getUser(String username) {
        return users.getOrDefault(username,null);

    }

}
编写登陆验证的类

(1)个人感觉类似Shiro里的自定义realm,这里自动实现授权和验证

(2)实现UserDetailsService接口里的loadUserByUsername方法,返回一个UserDetails的实现类,框架会自动调用UserDetails接口里的方法进行秘密校验,授权等等

(3)将该类注入到容器中

(4)此方法里返回一个UserDetail接口的对象,这就是为什么User实现UserDetail接口

代码语言:javascript
复制
package com.example.springsecuritydemo.service;

import com.example.springsecuritydemo.dao.UserDao;
import com.example.springsecuritydemo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

/**
 * @author CBeann
 * @create 2019-08-16 10:20
 */
@Component
public class MyUserDetailService implements UserDetailsService {

    @Autowired
    private UserDao userDao;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("登陆用户:" + username);

        //查询数据库
        User user = userDao.getUser(username);
        if(user==null){
            throw new UsernameNotFoundException("账号不存在:UsernameNotFoundException");
        }

        return user;

    }
}

自定义登陆页面

加入tymeleaf依赖

代码语言:javascript
复制
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

关闭缓存

代码语言:javascript
复制
spring.thymeleaf.cache=false

loginController

代码语言:javascript
复制
@Controller
public class LoginController {

    @RequestMapping("/login")
    public String login(){
        System.out.println("-----------login-------------");
        return "login/mylogin";

    }


}

在templates下创建 login/mylogin.html

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登陆</title>
</head>
<body>
<h1>登陆页面</h1>
<form method="post" action="/authentication/form">
    用户名:<input type="text"  name="username"/><br/>
    密码:<input type="password"  name="password"/><br/>
    <button  type="submit">登陆</button>
</form>
</body>
</html>

修改Security的配置类的configure方法

代码语言:javascript
复制
package com.example.springsecuritydemo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author CBeann
 * @create 2019-08-16 9:34
 * SpringSecurty核心配置
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    /**
     * 配置密码加密器,可以自定义加密器,实现PasswordEncoder接口
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new MyPasswordEncoder();
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //表单登陆
        http.formLogin()
                //http.httpBasic()//弹出框登陆
                //告诉系统自动定登陆页面
                .loginPage("/login")
                //告诉系统这个URL为登陆请求,系统会走登陆验证的过滤器
                .loginProcessingUrl("/authentication/form")
                .and()
                .authorizeRequests()//下面是请求配置
                .antMatchers("/login").permitAll()//当访问此URL(/login)时不需要验证
                .anyRequest()//任何请求
                .authenticated()//都要认证
                .and().csrf().disable();


    }
}

就可以用自定义的页面进行登陆了,此时有一个问题,登陆失败怎么回显???就是下面的自定义成功、失败处理

自定义登陆成功处理和自定义登陆失败处理

自定义登陆成功处理(比如把user对象存到session中,发送登陆成功短息)

(1)创建自定义登陆类

(2)继承SavedRequestAwareAuthenticationSuccessHandler,实现onAuthenticationSuccess方法

(3)在onAuthenticationSuccess方法中实现自己的成功登陆处理,比如用户加积分、登陆次数等等

(4)将类放在容器中

代码语言:javascript
复制
package com.example.springsecuritydemo.handler;


import com.example.springsecuritydemo.entity.User;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author CBeann
 * @create 2019-08-16 21:23
 */

@Component("myAuthenticationSuccessHandler")
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
                                        HttpServletResponse httpServletResponse,
                                        Authentication authentication) throws IOException, ServletException {
        System.out.println("登陆成功----------MyAuthenticationSuccessHandler");
        System.out.println(authentication);
        //获取用户的信息
        User loginUser = (User) authentication.getPrincipal();
        //给用户法发个积分,做个记录等操作
        //XXXService.method1()


        //调用框架原来的跳转
        super.onAuthenticationSuccess(httpServletRequest, httpServletResponse, authentication);

    }
}
自定义登陆失败处理(比如表单回显)

(1)创建自定义登陆类

(2)继承SimpleUrlAuthenticationFailureHandler,实现onAuthenticationFailure方法

(3)在onAuthenticationFailure方法中实现自己的失败处理,比如跳转到失败页面或者登陆页面

(4)将类放在容器中

代码语言:javascript
复制
package com.example.springsecuritydemo.handler;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author CBeann
 * @create 2019-08-17 9:32
 */
@Component("myAuthenticationFailureHandler")
public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {


        //不同的异常会有不同的信息,比如账号不存在、密码错误(坏的凭证)、账号不可用等等
        String message = exception.getMessage();


        //将错误信息回显到登陆页面
        request.setAttribute("msg", message);
        request.getRequestDispatcher("/login").forward(request, response);

//        response.setContentType("application/json;charset=UTF-8");
//        response.getWriter().write("登陆失败:"+message);


    }
}
修改Security配置类(显式的设置登陆成功、失败的处理器)
代码语言:javascript
复制
package com.example.springsecuritydemo.config;

import com.example.springsecuritydemo.service.MyUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

/**
 * @author CBeann
 * @create 2019-08-16 9:34
 * SpringSecurty核心配置
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    private MyUserDetailService myUserDetailService;

    /**
     * 解决UsernameNotFoundException不能被捕获的问题
     */
    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setHideUserNotFoundExceptions(false);
        provider.setUserDetailsService(myUserDetailService);
        provider.setPasswordEncoder(passwordEncoder);
        return provider;
    }


    /**
     * 配置密码加密器,可以自定义加密器,实现PasswordEncoder接口
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new MyPasswordEncoder();
    }

    @Autowired
    private PasswordEncoder passwordEncoder;


    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler;


    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //表单登陆
        http.formLogin()
                //http.httpBasic()//弹出框登陆
                //告诉系统自动定登陆页面
                .loginPage("/login")
                //告诉系统这个URL为登陆请求,系统会走登陆验证的过滤器
                .loginProcessingUrl("/authentication/form")//告诉系统登陆请求的url
                .successHandler(authenticationSuccessHandler)//自定义登陆成功处理
                .failureHandler(authenticationFailureHandler)//自定义登陆失败处理
                .and()
                .authorizeRequests()//下面是请求配置
                .antMatchers("/login").permitAll()//当访问此URL(/login)时不需要验证
                .anyRequest()//任何请求
                .authenticated()//都要认证
                .and().csrf().disable();


    }
}
登陆页面修改
代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登陆</title>
</head>
<body>
<h1>登陆页面</h1>
<span th:text="${msg}"></span>
<form method="post" action="/authentication/form">
    用户名:<input type="text" name="username"/><br/>
    密码:<input type="password" name="password"/><br/>
    <button type="submit">登陆</button>
</form>
</body>
</html>
测试

参考

Spring Security开发安全的REST服务-慕课网实战

链接:https://pan.baidu.com/s/1e7nsWoMT6EqCrZ5vhWE_XA 提取码:iwzt 复制这段内容后打开百度网盘手机App,操作更方便哦

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-03-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 构建项目
    • 前提
      • 目的
        • 感悟
          • 初始化项目SpringBoot+web+security
            • pom
            • 测试
            • 创建User实体类
            • 实现自定义密码加密器(本文没有做任何加密)
            • dao层(查询数据库)(此处用的静态数据)
            • 编写登陆验证的类
            • 自定义登陆成功处理(比如把user对象存到session中,发送登陆成功短息)
            • 自定义登陆失败处理(比如表单回显)
            • 修改Security配置类(显式的设置登陆成功、失败的处理器)
            • 登陆页面修改
            • 测试
        • 完善案例之数据库数据登陆(假数据)(模仿)
        • 自定义登陆页面
        • 自定义登陆成功处理和自定义登陆失败处理
        • 参考
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档