Spring Boot 整合 Spring Security 与 Shiro 以及两者的介绍与使用

Spring Security

介绍

Spring Security是一个功能强大、高度可定制性的身份验证和访问控制的框架。它能够保护基于Spring框架的应用程序。

Spring Security是一个专注于为 Java 应用程序提供身份验证和授权的框架。与所有Spring项目一样,Spring安全的真正威力在于它可以很容易地被扩展以满足需求。

整合流程

简单介绍整合流程:

本文案例使用 Spring Boot 集成 Spring Security。首先导入 security 的启动包,创建 security 的配置类,加上@EnableWebSecurity注解,并继承WebSecurityConfigurerAdapter抽象类,重写授权与用户认证的方法。
在授权方法中能够配置 用户访问某一 url 所具备的角色、前往登录页面的 url、注销成功后前往的 url、开启记住我功能 等。
在认证方法中可以配置 多个用户的用户名、密码、角色等信息、密码加密方式,用户信息可以从内存中获取,也能从数据库中获取。

代码结构与案例:
在这里插入图片描述
想要实现多个不同角色的用户想要动态访问 level1-level3 包下的页面,管理员所有页面都可访问,而其他用户通过角色来判断能否访问。

流程与代码:

首先加入启动器,当前 Spring Boot 版本使用 <version>2.2.6.RELEASE</version>

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

创建配置类,加上注解@EnableWebSecurity,继承WebSecurityConfigurerAdapter,重写方法:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	//认证方法
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
    }

	//授权方法
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
    }
}

授权方法:

  • authorizeRequests()授权多个请求
  • antMatchers("/").permitAll()配置 / 的 url 所有用户都可访问,当前案例 / 指向的就是首页
  • hasRole("XXX")配置访问该 url 所需的角色
  • formLogin()设定没有权限会默认去登录页面,默认访问 /login请求
  • loginPage("/toLogin")设定去登录页面的 url,没权限直接访问/toLogin请求
  • loginProcessingUrl("/login")设定点击登录按钮后访问的 url,与<form th:action="@{/login}" method="post">的请求路径一致,在 controller 中不需定义 login 方法,security 会自动帮我们进行登录操作
  • usernameParameter("XXX")passwordParameter("XXX")设置 form 登录表单中 <input type="text" name="userName">的 name 属性
@Override
    protected void configure(HttpSecurity http) throws Exception {
        //请求授权的规则
        http.authorizeRequests()
        		.antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("vip1")
                .antMatchers("/level2/**").hasRole("vip2")
                .antMatchers("/level3/**").hasRole("vip3");
                
        http.formLogin().loginPage("/toLogin")
        				.loginProcessingUrl("/login")
        				.usernameParameter("userName")
        				.passwordParameter("pwd");
        				
        /* csrf(跨站请求伪造) 注销时报404问题的原因之一 */
        http.csrf().disable();

		//设置注销成功去往的页面路径
        http.logout().logoutSuccessUrl("/");
        
        //开启rememberme功能
        http.rememberMe().rememberMeParameter("remember");
    }

认证方法:

  • withUser("admin")配置用户名
  • passwordEncoder(new XXXPasswordEncoder())设置密码加密方式,实现类例如:
    在这里插入图片描述
  • password(new BCryptPasswordEncoder().encode("123456"))设置加密方式与密码
  • roles("vip1", "vip2", "vip3")设置当前用户角色信息
  • and()用于拼接下一个用户
@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //inMemoryAuthentication() 从内存中获取认证信息
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin")
                .password(new BCryptPasswordEncoder().encode("123456"))
                .roles("vip1", "vip2", "vip3")
                .and()
                
                .withUser("zhangsan")
                .password(new BCryptPasswordEncoder().encode("123456"))
                .roles("vip1")
                .and()
                
                .withUser("lisi")
                .password(new BCryptPasswordEncoder().encode("123456"))
                .roles("vip1", "vip2");
    }

到这 security 配置类就写好了,用 admin 登录后:
在这里插入图片描述
当然 Thymeleaf 与 Spring Security 也可以整合,例如 zhangsan 用户只有 vip1 角色,在页面上应该只显示 level1 模块,Thymeleaf 如何在 html 页面中整合 Spring Security,看以下流程:

Thymeleaf 与 Spring Security 整合

首先导入整合依赖:

<dependency>
	<groupId>org.thymeleaf.extras</groupId>
	<artifactId>thymeleaf-extras-springsecurity5</artifactId>
	<version>3.0.4.RELEASE</version>
</dependency>

html 引入 springsecurity 命名空间:

<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">

div 标签中加入角色判断:

页面中是否显示的 div 块,加入sec:authorize="hasRole('vip1')",例如 level1:

<div class="column" sec:authorize="hasRole('vip1')">
 	<div class="ui raised segment">
		<div class="ui">
			<div class="content">
				<h5 class="content">Level 1</h5>
                <hr>
                <div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
                <div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
                <div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
			</div>
		</div>
	</div>
</div>

该案例的 github 链接:Spring Security

Shiro

介绍

Apache Shiro(发音为“shee roh”,日语中的“castle”一词)是一个功能强大且易于使用的Java安全框架,它执行身份验证、授权、加密和会话管理,可用于保护任何应用程序,从命令行应用程序,移动应用程序到最大的web和企业应用程序。

Shiro 提供了应用程序安全API来执行以下方面(将这些称为应用程序安全的四个基石):

  • 身份认证(Authentication) 证明用户身份,相当于用户登录
  • 授权(Authorization) 访问控制
  • 密码学(Cryptography) 保护或隐藏数据不被窥探
  • 会话管理(Session Management) 用户登录后就产生一次会话,退出之前会话中的信息都存在 session 中
    在这里插入图片描述

Shiro 架构

在这里插入图片描述

  • Subject:应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject,Subject代表当前的用户,这个用户不一定是具体的用户,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等,与Subject的所有交互都会委托给SecurityManager;Subject其实是一个门面,SecurityManager 才是实际的执行者。
  • SecurityManager:安全管理器,即所有与安全有关的操作都会与 SercurityManager 交互,并且它管理着所有的 Subject,可以看出它是 Shiro 的核心,它负责与 Shiro 的其他组件进行交互,它相当于 SpringMVC 的 DispatcherServlet 的角色。
  • Realm:该对象中配置了用户的认证与授权,SecurityManager从 Realm 对象中获取安全数据(如用户,角色,权限信息)来确定用户的身份是否合法,用户的操作是否能够进行。

整合流程:

大致流程:

  1. 导入 shiro 启动器依赖。
  2. 创建 Realm 类,继承抽象类AuthorizingRealm,并重写doGetAuthorizationInfodoGetAuthenticationInfo两个方法。
  3. 创建 shiro 配置类,在配置类中配置中注册三个 bean,三个 bean 分别是自定义的 Realm 对象、SecurityManager 对象、FilterFactoryBean 对象,在注册 FilterFactoryBean 的方法中配置访问 url 所需要的权限,与 Spring Security 中的授权方法类似。

代码结构与案例
在这里插入图片描述
案例实现拥有 add 权限的用户能进入 访问add.html,拥有 update 权限的用户能访问 update.html,实现登录功能

流程与代码:

导入 shiro 启动器依赖,当前 Spring Boot 版本为<version>2.2.6.RELEASE</version>

<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring-boot-starter</artifactId>
	<version>1.5.2</version>
</dependency>

配置Realm 类:

public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserMapper mapper;

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
       
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Subject currentUser = SecurityUtils.getSubject();
        User user = (User) currentUser.getPrincipal();
        //获取用户对应权限
        info.setStringPermissions(mapper.getUserPermission(user.getRoleId()));
        return info;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        //查询是否有这个用户
        User user = mapper.login(userToken.getUsername());
        if (user == null) {
            return null;
        }
        //user传入session中
        Subject currentUser = SecurityUtils.getSubject();
        Session session = currentUser.getSession();
        session.setAttribute("userName", user.getUserName());

        //进行密码认证
        return new SimpleAuthenticationInfo(user, user.getUserPassword(), "");
    }
}

shiro 配置类:

ShiroFilterFactoryBean 中需要使用 LinkedHashMap 去存储对应的 url 所需的权限信息

@Configuration
public class ShiroConfig {

    //ShiroFilterFactoryBean对象
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager);
        /*
        *   anon:无需认证就可以访问
            authc:必须认证了才能访问
            user: 必须拥有记住我功能才能用
            perms:拥有对某 个资源的权限才能访间;
            role:拥有某个角色权限才能访问
        * */
        Map<String, String> map = new LinkedHashMap<>();
        //定义请求是否需要权限
        map.put("/user/add", "perms[add]");
        map.put("/user/update", "perms[update]");
        bean.setFilterChainDefinitionMap(map);
        //定义去登录页面的请求
        bean.setLoginUrl("/toLoginPage");
        //定义未授权的请求
        bean.setUnauthorizedUrl("/unAuthorized");
        return bean;
    }

    //DefaultWebSecurityManager
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("getRealm") UserRealm realm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;
    }

    //Realm自定义对象
    @Bean
    public UserRealm getRealm() {
        return new UserRealm();
    }
}

写 controller 中的登录方法:

  • 用户点击登录后,会先进入 login 方法,将传过来的用户名与密码封装成一个 token 令牌,调用currentUser.login(token)方法后会进入 Realm 类中的认证方法,在认证方法内会去数据库查询用户名是否存在,判断密码是否错误,如果出现以上问题则抛出异常,异常被登录方法捕获,返回不同的 msg 错误信息,之后再会去 Realm 授权方法中获取用户的权限信息。
	@PostMapping("login")
    public String login(String name, String password, Model model) {
        Subject currentUser = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(name, password);
        try {
            currentUser.login(token);
            model.addAttribute("msg", "欢迎 " + name + " 登录");
            return "index";
        } catch (UnknownAccountException uae) {
            model.addAttribute("msg", "用户名不存在!");
            return "user/login";
        } catch (IncorrectCredentialsException ice) {
            model.addAttribute("msg", "密码错误!");
            return "user/login";
        }
    }

Shiro 与 Thymeleaf 的整合

仍旧导入整合依赖:

<dependency>
	<groupId>com.github.theborakompanioni</groupId>
	<artifactId>thymeleaf-extras-shiro</artifactId>
	<version>2.0.0</version>
</dependency>

在 html 的标签中加入 shiro 命名空间,例如:

<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">

html 标签中用 shiro:hasPermission 进行判断:

<div shiro:hasPermission="add">
    <a th:href="@{/user/add}">add</a>
</div>

<div shiro:hasPermission="update">
    <a th:href="@{/user/update}">update</a>
</div>

案例 GitHub 链接:Spring Boot Shiro

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值