0%

shiro整合springboot实现登录认证

shiro

Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。

可以看到:应用代码直接交互的对象是 Subject,也就是说 Shiro 的对外 API 核心就是 Subject;其每个 API 的含义:

Subject:主体,代表了当前 “用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject 认为是一个门面;SecurityManager 才是实际的执行者;

SecurityManager:安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;

Realm:域,Shiro 从从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。

也就是说对于我们而言,最简单的一个 Shiro 应用:

  1. 应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager;
  2. 我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。

从以上也可以看出,Shiro 不提供维护用户 / 权限,而是通过 Realm 让开发人员自己注入。(来源:w3cschool)

导入相关依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
      <dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
<!-- shiro整合thymeleaf -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

自定义Realm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class UserRealm extends AuthorizingRealm {

@Autowired
private UserServiceImpl userService;

//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了=》授权doGetAuthorizationInfo方法");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//获取当前登陆的用户对象,从认证方法传递过来
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();
//设置当前用户权限
info.addStringPermission(currentUser.getPerms());
return info;
}

//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

System.out.println("执行了=》认证doGetAuthenticationInfo方法");
//用户名密码,应该从数据库中读取
/* String username= "root";
String password = "123456";*/
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
User user = userService.queryByName(userToken.getUsername());

if (user == null){
return null;//会抛出异常 UnknownAccountException
}
//密码验证,交给shiro做

return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}

定义shiro配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Configuration
public class ShiroConfig {

@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);

//添加shiro的内置过滤器
/*
* anon:无需认证
* authc:必须认证
* user:必须拥有记住我功能才能访问
* perms:拥有对某个资源的权限才能访问
* role:拥有某个角色才能访问
* */
Map<String,String> filterMap = new LinkedHashMap<>();
//授权
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");
filterMap.put("/user/*","authc");
//使用自带注销功能
filterMap.put("/logout", "logout");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
shiroFilterFactoryBean.setLoginUrl("/toLogin");
//未授权页面
shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");
return shiroFilterFactoryBean;
}


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

//创建Realm对象,需要自定义类:1
@Bean
public UserRealm userRealm(){
return new UserRealm();
}

//用来整合thymeleaf和shiro: ShiroDialect
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}

前台首页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1> 首页 </h1>


<div shiro:notAuthenticated="">
<a th:href="@{/toLogin}">登录</a>
</div>

<div shiro:user="">
<a th:href="@{/logout}">注销</a>
</div>

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

</body>
</html>

实现