shiro安全框架+密码加密器

1、什么是shiro

Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.


Apache Shiro 是一个强大易用的 Java 安全框架,提供了认证、授权、加密和会话管理等功能,对于任何一个应用程序,Shiro 都可以提供全面的安全管理服务。并且相对于其他安全框架spring security,Shiro 要简单的多.

 Shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架。

1.2 为什么学shiro--简单--安全

Shiro可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在JavaEE环境。Shiro可以帮助我们完成:认证、授权、加密、会话管理、与Web集成、缓存等。

1.3 shiro的核心组件

  • Subject

Subject主体,外部应用与subject进行交互,subject将用户作为当前操作的主体,这个主体:可以是一个通过浏览器请求的用户,也可能是一个运行的程序。Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权

  • SecurityManager

SecurityManager权限管理器,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口

  • Authenticator

Authenticator即认证器,对用户登录时进行身份认证

  • Authorizer

Authorizer授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。

  • Realm(数据库读取+认证功能+授权功能实现)

Realm领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据
比如:
    如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。
注意:
    不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。 

  • SessionManager

SessionManager会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。

  • SessionDAO

SessionDAO即会话dao,是对session会话操作的一套接口
比如:
    可以通过jdbc将会话存储到数据库
    也可以把session存储到缓存服务器redis

  • CacheManager

CacheManager缓存管理,将用户权限数据存储在缓存,这样可以提高性能

  • Cryptography

Cryptography密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、md5等功能

1.4 使用shiro完成认证-ini

用户的信息存在--ini文件【实际开发存储数据库】。

(1)创建java的maven工程并引入shiro依赖

<dependencies>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.7.0</version>
        </dependency>
    </dependencies>

(2)创建一个ini文件

# [users]定义用户的信息--当对于表名叫users
[users]
# =左边表示用户名  =右边表示密码
zhangsan=123456
lisi=123456

(3)编写代码

public class ShiroTest {
    public static void main(String[] args) {
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        IniRealm realm = new IniRealm("classpath:shiro.ini");
        securityManager.setRealm(realm);
        SecurityUtils.setSecurityManager(securityManager);

        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("yhl","123456");
        try{
           subject.login(token);
            System.out.println("登陆成功");
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("登陆失败");
        }

    }
}

流程图:

1.5 使用shiro完成授权功能--ini

一定是认证后才能对权限判断  

(1)修改ini文件

[users]
yhl=123456,admin
ljh=123456,role1

[roles]
admin=user:query,user:update,user:delete,user:insert
role1=user:export,user:delete

 (2)修改代码

public class ShiroTest02 {
    public static void main(String[] args) {
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        IniRealm realm = new IniRealm("classpath:shiro.ini");
        securityManager.setRealm(realm);//指定realm
        SecurityUtils.setSecurityManager(securityManager);//连接上下文,不然不生效

        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("ljh","123456");
        try {
            subject.login(token);
            System.out.println("登陆成功");
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("登陆失败");
        }
//        subject.logout();//退出之后就查询不到权限了


        System.out.println("---------------------------");
        boolean permittedAll = subject.isPermittedAll("user:export", "user:delete");//是否同时具有所有的权限
        System.out.println("是否同时具有所有的权限:" + permittedAll);
        boolean[] permitted = subject.isPermitted("user:export", "user:delete", "user:query", "user:insert");
        System.out.println("判断用户具有的权限:" + Arrays.toString(permitted));
        boolean b = subject.isPermitted("user:query");
        System.out.println("判断用户是否具有query:" + b);

    }
}

流程图:  

1.6 使用shiro完成认证--数据源

上面我们通过读取 ini 文件,可以获取数据源。 IniRealm 完成的读取并授权。而现在如果要读取数据源的数据完成认证授权,则需要自定义realm 类。
自己在service中造了一些数据!!!! 

 (1)引入依赖

<dependencies>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.28</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>22.0</version>
        </dependency>
    </dependencies>

 并且创建实体类~!

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String username;
    private String password;
    private String address;

}

(2)创建一个MyRealm类并继承AuthorizingRealm

public class MyRealm extends AuthorizingRealm {
    private UserServiceImpl userService=new UserServiceImpl();
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了授权方法----------------------");
        //通过getPrimaryPrincipal可以获得传过来的信息
        String primaryPrincipal = principalCollection.getPrimaryPrincipal().toString();
        System.out.println(primaryPrincipal);
        //通过查询出来的权限放到一个List集合中
        List<String> list = userService.findByUsername(primaryPrincipal);
        //判断list的长度判断是否有权限
        if (list.size()!=0){
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //将数据封装到SimpleAuthenticationInfo里面
            info.addStringPermissions(list);
            return info;
        }
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了认证方法----------------------");
        String username = authenticationToken.getPrincipal().toString();//获取当前登陆的用户名
        User user = userService.findUsername(username);
        if (user!=null){
            //将数据封装到SimpleAuthenticationInfo里面
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,user.getPassword(),this.getName());
            return info;
        }
        return null;
    }
}
(3)UserService
public class UserServiceImpl {
    public User findUsername(String username){
        if ("yhl".equals(username)){
            return new User(1,"yhl","123456","北京");
        }else if ("ljh".equals(username)){
            return new User(1,"ljh","123","上海");
        }
        return null;
    }

    public List<String> findByUsername(String username) {
        //自己添加一些数据让service查出来,看看效果
        ArrayList<String> list = new ArrayList<>();
        if ("yhl".equals(username)){
            list.add("user:query");
            list.add("user:delete");
            list.add("user:insert");
            list.add("user:update");
        }else if ("ljh".equals(username)){
            list.add("user:export");
            list.add("user:delete");
        }
        return list;
    }
}

(4)测试类

public class Test01 {
    public static void main(String[] args) {
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        MyRealm myRealm = new MyRealm();//使用自己定义的Realm
        securityManager.setRealm(myRealm);
        SecurityUtils.setSecurityManager(securityManager);

        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("yhl","123456");
        try{
            subject.login(token);//不管有没有登陆成功都会执行认证方法的
            System.out.println("登陆成功");
        }catch (Exception e){
            System.out.println("登陆失败");
        }

        boolean permitted = subject.isPermitted("user:query");//登陆成功后才会执行授权方法
        System.out.println("yhl是否具有query:" + permitted);
    }
}

2、密码加密器

在数据库中存储的密码是一个密文。

shiro帮我们提供很多种加密器。---如果没有指定加密器,那么都使用默认的无加密器。

public class Test {
    public static void main(String[] args) {
//        Md5Hash md5Hash1=new Md5Hash("123456");
//        System.out.println(md5Hash1); //别人会把一些场景的明文加密后的密文存储的一个很大的数据库。
//        Md5Hash md5Hash2=new Md5Hash("123456","aaa"); //盐
//        System.out.println(md5Hash2);
//        Md5Hash md5Hash=new Md5Hash("123456","aaa",3);//3:表示加密次数
//        System.out.println(md5Hash);

        SimpleHash simpleHash=new SimpleHash("SHA","123456","aaa",1024);
        SimpleHash simpleHash2=new SimpleHash("MD5","123456","aaa",1024);
        System.out.println(simpleHash);
        System.out.println(simpleHash2);
    }
}

2.2 把密码匹配器使用在shiro

(1)修改userService中的代码

(2)修改测试代码

 (3)myRealm代码

猜你喜欢

转载自blog.csdn.net/yhl15736773842/article/details/131556195