目次
皆さん、こんにちは。エンタープライズレベルのプロジェクトを作成するときは、基本的に ID 認証と認可を含む権限が必要です。
いわゆる本人認証とは、あなたがあなたであることを証明するものです。
いわゆる認証とは、ログイン後に何ができるかを理解することです。
さて、springboot プロジェクトから始めて、それを hiro フレームワークと組み合わせてすべてを実行しましょう。
環境構築
基本情報:
スプリングブートのバージョン:
初期のその他の依存関係:
作成した:
Shiro と Spring Boot の依存関係を追加します。次の依存関係を pom.xml ファイルに追加します。
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.7.1</version>
</dependency>
hiro環境を設定する
最初のステップは、Shiro 構成クラスを作成することです。
クラスを作成し、 @Configuration アノテーションを付けてマークします。このクラスでは、レルムやセッションマネージャーなど、Shiro に関連する設定を行うことができます。
設定コードは次のとおりです。
package com.it.shirodemo.config;
import com.it.shirodemo.realm.MyRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShiroConfig {
@Bean
public MyRealm myRealm(){
return new MyRealm();
}
@Bean(name = "mySecurityManager")
public DefaultWebSecurityManager securityManager(@Qualifier("myRealm") MyRealm myRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm);
return securityManager;
}
/**
* 路径过滤规则
* @return
*/
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
//登录不需要校验权限
chainDefinition.addPathDefinition("/login", "anon");
//其他任何url都需要校验是否登录
chainDefinition.addPathDefinition("/**", "authc");
return chainDefinition;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("mySecurityManager") SecurityManager securityManager) {
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
filterFactoryBean.setSecurityManager(securityManager);
filterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition().getFilterChainMap());
return filterFactoryBean;
}
/**
* 开启Shiro注解模式,可以在Controller中的方法上添加注解
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("mySecurityManager") SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
これらは比較的固定されたコードであり、各Beanの意味を知る必要もなく、過去のものをコピーしてそのまま使用しても問題ありません。
次に、レルムをカスタマイズする必要があります。
コード
package com.it.shirodemo.realm;
import com.it.shirodemo.entity.User;
import com.it.shirodemo.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import javax.annotation.Resource;
public class MyRealm extends AuthorizingRealm {
@Resource
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 实现授权逻辑
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//获得当前subject
Subject subject = SecurityUtils.getSubject();
//获得当前的principal,也就是认证完后我们放入的信息
User currentUser = (User) subject.getPrincipal();
//添加权限
info.addStringPermissions(currentUser.getPerms());
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 实现认证逻辑
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
//从数据库中查询该用户
String username = usernamePasswordToken.getUsername();
User user = userService.queryUserByName(username);
//如果不存在该用户,返回一个空错误,前端也可以相应显示提示
if (user == null) {
return null;
}
// 第一个参数为principal,就是授权方法里面拿到的那个对象;
// 第二个参数为从数据库中查出的用于验证的密码,shiro中密码验证不需要我们自己去做;
// 第三个参数为 realmName
return new SimpleAuthenticationInfo(user,user.getPwd(),getName());
}
}
これは主に認可と認証メソッドを記述します。上記はdoGetAuthorizationInfo
認可に使用されます。システムの特定の URL にアクセスするたびに呼び出されます。目的は、現在のユーザーの権限を取得することです。
User クラスを見てみましょう。
コード
package com.it.shirodemo.entity;
import lombok.Data;
import java.util.Set;
@Data
public class User {
private String userName;
private String pwd;
private Set<String> perms;
}
perms は Set コレクションです。シミュレートされたサービスで初期化してクエリを実行します。
package com.it.shirodemo.service;
import com.it.shirodemo.entity.User;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class UserService {
public User queryUserByName(String username) {
List<User> users = new ArrayList<>();
users.add(new User(){
{
setUserName("admin");
setPwd("123");
setPerms(new HashSet<String>(){
{
add("shiro:user-query");
add("shiro:user-add");
add("shiro:user-delete");
add("shiro:user-edit");
}});
}});
users.add(new User(){
{
setUserName("zhangsan");
setPwd("123");
setPerms(new HashSet<String>(){
{
add("shiro:user-query");
}});
}});
List<User> userList = users.stream().filter(e -> e.getUserName().equals(username)).collect(Collectors.toList());
if(userList.size() > 0){
return userList.get(0);
}
return null;
}
}
テストのために、ユーザーのコントローラーを特別に作成しました
package com.it.shirodemo.controller;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/v1/user/")
public class UserController {
@GetMapping("/query")
@RequiresPermissions("shiro:user-query")
public String query(){
return "用户查询";
}
@GetMapping("/edit")
@RequiresPermissions("shiro:user-edit")
public String edit(){
return "用户修改";
}
@GetMapping("/delete")
@RequiresPermissions("shiro:user-delete")
public String delete(){
return "用户删除";
}
@GetMapping("/add")
@RequiresPermissions("shiro:user-add")
public String add(){
return "用户新增";
}
}
管理ユーザーはすべての権限にアクセスでき、zhangsan はユーザーがクエリするインターフェイスにのみアクセスできることが想定されます。
実行されました
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 实现授权逻辑
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//获得当前subject
Subject subject = SecurityUtils.getSubject();
//获得当前的principal,也就是认证完后我们放入的信息
User currentUser = (User) subject.getPrincipal();
//添加权限
info.addStringPermissions(currentUser.getPerms());
return info;
}
この認可メソッドの後、返される認可情報オブジェクトにはすべての権限のリストが含まれます。たとえば、現在のアクセス権限が含まれている場合はshiro:user-add
アクセスが許可され、それ以外の場合はアクセスが許可されません。
もう一度認証方式を見てみますと、認証とは端的に言うとログイン認証のことです。
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 实现认证逻辑
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
//从数据库中查询该用户
String username = usernamePasswordToken.getUsername();
User user = userService.queryUserByName(username);
//如果不存在该用户,返回一个空错误,前端也可以相应显示提示
if (user == null) {
return null;
}
// 第一个参数为principal,就是授权方法里面拿到的那个对象;
// 第二个参数为从数据库中查出的用于验证的密码,shiro中密码验证不需要我们自己去做;
// 第三个参数为 realmName
return new SimpleAuthenticationInfo(user,user.getPwd(),getName());
}
ここで非常に興味深いのが、最終的に返される SimpleAuthenticationInfo オブジェクトです。構築メソッドの最初のパラメータは Object 型です。
これは実際には次のとおりです。
つまり、認証メソッドでどのようなパラメータを渡し、認可メソッドで取得されるプリンシパルはどのようなオブジェクトになるのかということです。
オブジェクトを渡すためにこのような開口部を提供する理由は、実際には、認可時に現在のユーザーの認可リストを取得できるようにするためです。
インターネット上で hiro に関する記事をたくさん見てきましたが、この点についての理解はかなり曖昧ですが、とにかく、このパラメータに何を渡しても、それを介して許可リストを取得できなければならないことだけは覚えておいてください。たとえば、ここでは User オブジェクトを渡しますが、User オブジェクトにはすでに Perm が設定されているので問題ありません。承認時にユーザー名を渡してデータベースにアクセスして権限リストを照会することもできますが、まったく問題ありません。
最後に、ログイン方法を指定します。
package com.it.shirodemo.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LoginController {
@GetMapping("/login")
public String login(String username,String password){
Subject subject = SecurityUtils.getSubject();
//令牌
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
//登录认证
subject.login(token);
return "恭喜,登录成功!";
}catch (UnknownAccountException e){
return "账号不存在";
}catch (IncorrectCredentialsException e){
return "密码错误";
}
}
}
確認するには、ブラウザを開きます。これは Get リクエストであるため、ブラウザを使用して直接送信します。
http://localhost:8080/login?username=admin&password=123
次に、http://localhost:8080/v1/user/add にアクセスします。
Zhang San がログインしている場合は、http://localhost:8080/v1/user/add にアクセスしてください。
バックグラウンドでエラーが報告されました:
org.apache.hiro.authz.AuthorizationException: メソッドを呼び出す権限がありません: public java.lang.String com.it.hirodemo.controller.UserController.add()
要約する
この記事では、いわゆる ACL 権限制御を実装します。これは、hiro フレームワークと springboot を組み合わせて実装されており、初心者が学ぶのに非常に適しています。
ソースコードダウンロード https://gitee.com/skyblue0678/shiro-demo