程序员都在用的Shiro框架,快来看看!!!

何为Shiro

Apache Shiro 是一个开源、轻量级的 Java 安全框架,它提供身份验证、授权、密码管理以及会话管理等功能。相对于 Spring Security,Shiro 框架更加直观、易用,同时也能提供健壮的安全性。

在传统的 SSM 框架中,手动整合 Shiro 的配置步骤还是比较多的,针对 Spring Boot,Shiro 官方提供了 shiro-spring-boot-web-starter 用来简化 Shiro 在 Spring Boot 中的配置。也就是可以引入以下依赖。

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

为什么要使用Apache Shiro?

自2003年以来,框架格局发生了很大变化,因此今天仍然有充分的理由使用Shiro。实际上有很多原因。Apache Shiro特点如下:

  1. 易于使用 :易于使用是该项目的最终目标。应用程序安全性可能非常令人困惑和沮丧,并被视为“必要的邪恶”。如果您使它易于使用,以使新手程序员可以开始使用它,那么就不必再痛苦了。
  2. 全面 :没有其他安全框架可以达到Apache Shiro宣称的广度,它可以为你的安全需求提供“一站式”服务。
  3. 灵活 :Apache Shiro可以在任何应用程序环境中工作。Shiro也不要求任何规范,甚至没有很多依赖性。
  4. 具有Web功能 :Apache Shiro具有出色的Web应用程序支持,允许您基于应用程序URL和Web协议(例如REST)创建灵活的安全策略。

示例程序

application.properties 中配置

Shrio基本的配置信息如下,有些使用默认就好了。

# 配置登录地址,默认为"login.jsp"
shiro.loginUrl=/login
# 配置登录成功地址,默认为"/"
shiro.successUrl=/index
# 配置未获授权默认跳转地址
shiro.unauthorizedUrl=/unauthorized
# 是否允许通过 URL 参数实现会话跟踪,默认为 true。如果网站支持 Cookie,可以关闭次选项。
shiro.sessionManager.sessionIdUrlRewritingEnabled=true
# 是否允许通过 Cookie 实现会话跟踪,默认为 true。
shiro.sessionManager.sessionIdCookieEnabled=true

接下来配置Realm,Realm 充当了 Shiro 与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro 会从应用配置的 Realm 中查找用户及其权限信息。从这个意义上讲,Realm 实质上是一个安全相关的 DAO,它封装了数据源的连接细节,并在需要时将相关数据提供给 Shiro。

有两个重要的方法需要介绍:

  1. 身份验证(getAuthenticationInfo 方法)验证账户和密码,并返回相关信息

  2. 权限获取(getAuthorizationInfo 方法) 获取指定身份的权限,并返回相关信息

public class MyRealm extends AuthorizingRealm {
    /**
     * 系统存在的用户列表
     */
    private List<String> userList= Stream.of("user1","user2","user3","user3").collect(Collectors.toList());


    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());


        if (!userList.contains(username) || !password.equals("123456")){
            throw new UnknownAccountException("用户名或密码错误");
        }
        return new SimpleAuthenticationInfo(username, password, getName());
    }
}

配置Shiro,关于addPathDefinition参数的第二个参数取值如下:

anon: 无需认证即可访问
authc: 需要认证才可访问
user: 点击“记住我”功能可访问
perms: 拥有权限才可以访问
role: 拥有某个角色权限才能访问

也就是我们让doLogin接口可以无需认证即可访问,其余的需要认证才能访问。

@Configuration
public class ShiroConfig {
    @Bean
    public  MyRealm myRealm() {
        return new MyRealm();
    }
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(myRealm());
        return manager;
    }
    @Bean
    public  ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
        definition.addPathDefinition("/doLogin", "anon");
        definition.addPathDefinition("/**", "authc");
        return definition;
    }
}

在控制器中通过接收到的用户名和密码构造一个 UsernamePasswordToken,然后获取到Subject对象,调用login执行登录操作。

@RestController
public class LoginController {
    /**
     * 登录
     *
     * @param username
     * @param password
     */
    @PostMapping("/doLogin")
    public void doLogin(String username, String password) {
        Subject subject = SecurityUtils.getSubject();
        subject.login(new UsernamePasswordToken(username, password));
        System.out.println("登录成功!");

    }

    @GetMapping("/index")
    public String hello() {
        return "hello";
    }

    @GetMapping("/login")
    public String login() {
        return "{\"message\":\"请登录\"}";
    }
}

全局异常

@RestControllerAdvice
public class ErrorController {

    @ExceptionHandler(Exception.class)
    public R handlerException(Exception e){
        return  new R(-1,e.getMessage(),"{}");
    }
    @ExceptionHandler(UnknownAccountException.class)
    public R handlerUnknownAccountException(Exception e){
        return  new R(-1,e.getMessage(),"{}");
    }
}

运行效果

在这里插入图片描述

在这里插入图片描述

扩展

如果此时某个接口只允许有指定角色的人才能方法的话,可以这样做。

首先使用RequiresRoles注解标明需要具有admin角色的用户才能访问。

@GetMapping("listUser")
@RequiresRoles(value = {"admin"})
public List<String> listUser(){
    return Stream.of("1","2","2").collect(Collectors.toList());
}

在doGetAuthorizationInfo中处理逻辑,如果用户是admin1、admin2,则将他们赋予admin角色,也就是只有他两才具有访问listUser的权限。

public class MyRealm extends AuthorizingRealm {
    /**
     * 系统存在的用户列表
     */
    private List<String> userList= Stream.of("user1","user2","user3","user3","admin1","admin2").collect(Collectors.toList());


    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String userName = (String) principals.getPrimaryPrincipal();

        if ("admin1".equals(userName)||"admin2".equals(userName)){
            Set<String> rol =new HashSet<>();
            rol.add("admin");
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(rol);
            return simpleAuthorizationInfo;
        }
        return null;
    }
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());


        if (!userList.contains(username) || !password.equals("123456")){
            throw new UnknownAccountException("用户名或密码错误");
        }
        return new SimpleAuthenticationInfo(username, password, getName());
    }
}

首先这是普通用户登录后访问的提示。
在这里插入图片描述
在使用admin1用户名进行登录后的截图。
在这里插入图片描述
另外RequiresRoles注解中Logical的值默认是Logical.AND。这个值表明用户要具有指定所有角色才允许访问,如果我们想只要满足其中一个角色即可访问的话需要更改为Logical.OR。

@GetMapping("listUser")
@RequiresRoles(value = {"admin","teacher"},logical = Logical.OR)
public List<String> listUser(){
    return Stream.of("1","2","2").collect(Collectors.toList());
}

类似还有其他注解
@RequiresAuthentication
要求当前Subject 已经在当前的session 中被验证通过才能被注解的类/实例/方法访问或调用

@RequiresGues
要求当前的Subject 是一个“guest”,也就是他们必须是在之前的session中没有被验证或记住才能被注解的类/实例/方法访问或调用

@RequiresPermissions
要求当前的Subject 被允许一个或多个权限,以便执行注解的方法。

@RequiresUser
需要当前的Subject 是一个应用程序用户才能被注解的类/实例/方法访问或调用。要么是通过验证被确认,或者在之前session 中的’RememberMe’服务被记住

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Shiro 是一个强大易用的Java安全框架,可以帮助我们实现身份认证、授权、会话管理等安全相关功能。在 Spring Boot + Vue 商城中,我们可以使用 Shiro 框架来实现用户账户注册功能。 下面是一些实现步骤: 1. 添加 Shiro 相关依赖 在 pom.xml 文件中添加 Shiro 相关依赖: ``` <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-web-starter</artifactId> <version>1.7.1</version> </dependency> ``` 2. 配置 Shiro 在 application.yml 文件中添加 Shiro 的配置: ``` # Shiro 配置 shiro: # 登录 URL login-url: /login # 登录成功 URL success-url: / # 未授权 URL unauthorized-url: /unauthorized # 过滤器链定义 filter-chain-definition-map: /login: anon /register: anon /logout: logout /static/**: anon /**: authc ``` 其中,filter-chain-definition-map 定义了 URL 的访问权限,anon 表示该 URL 可以匿名访问,authc 表示该 URL 需要登录后才能访问。 3. 实现用户注册功能 在用户注册的 Controller 中,我们可以使用 Shiro 的 Subject 对象来进行身份认证和授权操作。具体代码如下: ``` @PostMapping("/register") public Result register(@RequestBody User user) { // 创建用户信息 User newUser = new User(); newUser.setUsername(user.getUsername()); newUser.setPassword(user.getPassword()); newUser.setRoles("user"); // 对用户密码进行加密 String salt = UUID.randomUUID().toString(); String passwordHash = new SimpleHash("md5", newUser.getPassword(), salt, 2).toHex(); newUser.setPassword(passwordHash); newUser.setSalt(salt); // 保存用户信息 userRepository.save(newUser); // 登录用户 UsernamePasswordToken token = new UsernamePasswordToken(newUser.getUsername(), newUser.getPassword()); Subject subject = SecurityUtils.getSubject(); subject.login(token); // 返回结果 return Result.success("注册成功"); } ``` 在上述代码中,我们通过 Shiro 的 Subject 对象进行了用户登录操作,登录成功后,用户就可以访问需要登录才能访问的 URL 了。 以上就是使用 Shiro 框架Spring Boot + Vue 商城中实现用户账户注册的步骤。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值