Spring Security安全框架的介绍和案例使用


Spring Security介绍:

Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是用于保护基于Spring的应用程序的事实上的标准。

Spring Security是一个框架,致力于为Java应用程序提供身份验证和授权。像所有Spring项目一样,Spring Security的真正强大之处在于它可以轻松扩展以满足定制需求的能力。

Spring Security特性:

  • 对身份验证和授权的全面且可扩展的支持

  • 保护免受会话固定,点击劫持,跨站点请求伪造等攻击

  • Servlet API集成

  • 与Spring Web MVC的可选集成
    。。。。。。。


案例Springboot整合SpringSecurity

这里案例是我使用security做的一个demo,仅供参考。

目录结构:
在这里插入图片描述

1.pom.xml导入依赖

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-security</artifactId>
 </dependency>
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-thymeleaf</artifactId>
 </dependency>
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
     <groupId>org.thymeleaf.extras</groupId>
     <artifactId>thymeleaf-extras-springsecurity5</artifactId>
 </dependency>
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-devtools</artifactId>
     <scope>runtime</scope>
     <optional>true</optional>
 </dependency>
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-configuration-processor</artifactId>
     <optional>true</optional>
 </dependency>
 <dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
     <optional>true</optional>
 </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>
 <!--数据库依赖-->
 <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>8.0.23</version>
 </dependency>
 <dependency>
     <groupId>com.baomidou</groupId>
     <artifactId>mybatis-plus-boot-starter</artifactId>
     <version>3.3.2</version>
 </dependency>

2.application.properties文件

#spring数据源 2.0后默认使用hikari连接池
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/blb_erp?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456

#编码问题
server.servlet.encoding.charset=utf-8
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
server.tomcat.uri-encoding=UTF-8

##日志级别
logging.level.com.yang.springboot208springsecurity.mapper=debug

#关闭thymeleaf的缓存
#spring.thymeleaf.cache=false

##mybatis-plus mapper xml 映射地址
mybatis-plus.mapper-locations=classpath:mappers/*.xml
##mybatis-plus type-aliases 别名
mybatis-plus.type-aliases-package=com.yang.springboot208springsecurity.entity

3.创建需要的页面

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>登录界面</h3>
<span th:if="${param.error}">账号密码错误</span>
<form action="/toLogin" method="post"><!--/login是security自带的登录接口-->
    <!--如果不配置 username和password那名字必须是这两个-->
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <!--解决csrf跨站攻击 不用加 .csrf.disable()-->
    <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
    <input type="radio" name="remember">记住我<br>
    <input type="submit" value="登录">
</form>
</body>
</html>

main.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h2>主页面</h2>
    <h3 th:text="'Hello '+${username}"></h3>
    <span th:each="auth : ${authorities}">
        [[${auth}]]
    </span>
    <p><a href="/admin/admin">访问管理员页面</a></p>
    <p><a href="/user/user">访问用户页面</a></p>
    <form action="/logout" method="post">
        <input type="submit" value="退出">
    </form>
</body>
</html>

error.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h2>权限不足请重新登录:<a th:href="@{/toLogin}">登录</a> </h2>
</body>
</html>

user.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h3>欢迎来到用户界面</h3>
    <a href="/logout">返回登录</a>
</body>
</html>

admin.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h3>欢迎来到管理员界面</h3>
    <a href="/logout">返回登录</a>
</body>
</html>

4.实体类

User.java

@Data
public class User {
  private long id;
  private String username;
  private String password;
  private String realname;
  private long orgId;
  private String salt;
  private String telephone;
  private String icon;
  private long state;
  private long siteId;
}

Role.java

/**
 * 角色实体类
 */
@Data
public class Role {

  private long id;
  private String name;
  private String description;
  private long siteId;
}

Permission.java

/**
 * 权限实体类
 */
@Data
public class Permission {

  private long id;
  private String name;
  private String description;
  private String url;
  private long pid;
  private String icon;
  private long sort;
}

### 5. Mapper层:
UserMapper.java

public interface UserMapper extends BaseMapper<User> {
    List<Role> selectRoleByUsername(String username);
    List<Permission> selectPermissionByUsername(String username);
}

UserMapper.xml映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yang.springboot208springsecurity.mapper.UserMapper">
    <select id="selectRoleByUsername" parameterType="String" resultType="Role">
        select r.* FROM
        user u JOIN user_role ur ON u.id=ur.user_id
        JOIN role r ON r.id=ur.role_id
        where u.username=#{username}
    </select>
    <select id="selectPermissionByUsername" parameterType="String" resultType="Permission">
        select p.* FROM
        user u JOIN user_role ur ON u.id=ur.user_id
        JOIN role r ON r.id=ur.role_id
        JOIN role_permission rp on rp.role_id=r.id
        JOIN permission p ON p.id=rp.fun_id
        where u.username=#{username}
    </select>
</mapper>

6.service层

UserService接口:

public interface UserService extends IService<User> {
}

UserServiceImpl实现类:

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

UserDetailsServiceImpl:

/**
 * 自定义登录权限逻辑实现
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Resource
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        /*获取用户信息通过用户名*/
        User user = userMapper.selectOne(new QueryWrapper<User>().lambda().eq(User::getUsername, s));
        if (user==null){
            throw  new UsernameNotFoundException("用户不存在");
        }
        /*查询用户角色*/
        List<Role> roles = userMapper.selectRoleByUsername(s);
        /*查询用户权限*/
        List<Permission> permissions = userMapper.selectPermissionByUsername(s);
        StringBuilder string = new StringBuilder();
        /*将权限和角色封装成一个字符串*/
        roles.forEach(role->{string.append("ROLE_"+role.getName()+",");});
        permissions.forEach(permission -> {string.append(permission.getName()+",");});
        if(string.length()>0){
            string.deleteCharAt(string.length()-1);
        }
        /*创建security的User包装用户名密码和权限*/
        org.springframework.security.core.userdetails.User user1 = new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
                AuthorityUtils.commaSeparatedStringToAuthorityList(string.toString()));
        return  user1;
    }
}

7.配置类

RememberMeConfig.java

/**
 * 记住我配置
 */
@Configuration
public class RememberMeConfig {
    /*注入数据源*/
    @Autowired
    private DataSource dataSource;

    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        //创建记录用户登录的表persistent_logins 如果存在会报错
        //jdbcTokenRepository.setCreateTableOnStartup(true);
        return jdbcTokenRepository;
    }
}

SecurityConfig.java:

/**
 *  websecurity配置
 */
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /*注入自定义用户验证*/
    @Resource
    private UserDetailsService userDetailsService;
    @Autowired
    private PersistentTokenRepository persistentTokenRepository;
    /**
     * security登录验证必须经过加密处理 passwordEncoder接口进行面没加密
     * @return 返回密码编码器
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 认证规则 用户名密码和角色  (用户名和密码可以利用数据库来验证)
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        /*//定义用户名和密码和用户权限
        auth.inMemoryAuthentication()
                //第一个用户配置  角色设置
                .withUser("yang").password(new BCryptPasswordEncoder().encode("123456")).roles("user","admin")
                .and()
                //第二个用户配置 角色设置
                .withUser("wang").password(new BCryptPasswordEncoder().encode("123456")).roles("user");
        */
        /*使用自定义用户验证和授权*/
        auth.userDetailsService(userDetailsService);
    }

    /**
     * 配置页面的授权
     * @param http
     * @throws Exception
     */
    @Override //定制权限规则
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests() //配置页面的授权
                .antMatchers("/login","/toLogin","/js/**","/**/*.css","/hello").permitAll() //前面的所有url不用验证
                .antMatchers("/admin/**").hasRole("管理员") //该路径对应的角色
                .antMatchers("/user/**").hasAuthority("销售管理") //该路径对应的权限也可以放角色 ROLE
                .anyRequest().authenticated() //除了前面的url 都要验证
                .and()
                .formLogin()//登录相关配置
                .loginPage("/toLogin") //失败后跳转的url
                .successForwardUrl("/toMain")//登录成功后跳转的访问url
                .usernameParameter("username") //用户名参数名
                .passwordParameter("password") //密码参数名
                .and()
                .logout().logoutUrl("/logout")//登录退出的访问url 必须是post请求
                .logoutSuccessUrl("/toLogin") //退出页面
                .and()
                .rememberMe()
                .rememberMeParameter("remember") //表单参数名
                .tokenRepository(persistentTokenRepository) //设置数据源
                .tokenValiditySeconds(60); //设置记住生命周期
                //.and()
                //.csrf().disable();//生产环境下禁用跨域攻击防御,不然登录提交失败
                //如果开启需要请求中携带_csrf=tokenxxxx否者登录会失败
    }
}

8.Controller层:

@Controller
public class UserController {
    @RequestMapping("/toLogin")
    public String toLogin(){
        return "login";
    }
    @RequestMapping("/toMain")
    public String toMain(Model model){
        /*获得当前用户的验证信息*/
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Object principal = authentication.getPrincipal();
        if (principal instanceof org.springframework.security.core.userdetails.User){
            User user = (User) principal;
            model.addAttribute("username",user.getUsername());
            model.addAttribute("authorities",user.getAuthorities());
        }
        return "main";
    }
    @RequestMapping("/admin/admin")
    public String toAdmin(){
        return "admin/admin";
    }
    @RequestMapping("/user/user")
    public String toUser(){
        return "user/user";
    }
    @RequestMapping("/logout")
    public String toLogout(){
        return "login";
    }
}

9.springboot主启动类

@MapperScan(basePackages = "com.yang.springboot208springsecurity.mapper")
@SpringBootApplication
public class Springboot208SpringsecurityApplication {
    public static void main(String[] args) {      SpringApplication.run(Springboot208SpringsecurityApplication.class, args);
    }
}

10.运行结果:

在这里插入图片描述

在这里插入图片描述

不同的用户有不同的权限有的页面是进不去的:
在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

记或往

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

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

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

打赏作者

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

抵扣说明:

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

余额充值