需求:
① 除了登录页面,在地址栏直接访问其他URL,均跳转至登录页面
② 登录涉及帐号和密码,帐号错误提示帐号错误,密码错误提示密码错误
③ 登录成功跳转至首页,首页显示登录者帐号信息,并有注销帐号功能,点击注销退出系统
分析:
典型的运用认证权限的需求,考虑使用Shiro。了解一下Shiro框架,Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。
关键词汇:
① Subject:安全术语,本意是“当前的操作用户”。
在安全领域,术语“Subject”可以是人,也可以是第三方进程、后台帐户(Daemon Account)、定时作业(Corn Job)或其他类似事物。
它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。
在程序中能轻易获得Subject,允许在任何需要的地方进行安全操作。
每个Subject对象都必须与一个SecurityManager进行绑定,访问Subject对象其实都是在与SecurityManager里的特定Subject进行交互。
② SecurityManager:安全管理器。
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
③ Realm:域,Shiro从Realm获取安全数据(如用户、角色、权限),
即SecurityManager要验证用户身份,需要从Realm获取相应用户进行比较以确定用户身份是否合法;
也就是说需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,安全数据源。
④ authentication:认证(发音:[ɔ:ˌθentɪ'keɪʃn])
⑤ authorization:授权(发音:[ˌɔ:θərəˈzeɪʃn])
0、数据库建表init.sql
1 DROP TABLE sys_user; 2 3 CREATE TABLE sys_user 4 ( 5 userid INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户编号', 6 username VARCHAR(10) NOT NULL COMMENT '用户名称', 7 `password` VARCHAR(10) NOT NULL COMMENT '用户密码' 8 ); 9 10 INSERT INTO sys_user VALUES(NULL, 'admin', '123'), (NULL, 'test', '456'); 11 12 SELECT * FROM sys_user;
1、编写项目对象模型文件pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 7 <groupId>cn.temptation</groupId> 8 <artifactId>studyShiro</artifactId> 9 <version>1.0-SNAPSHOT</version> 10 11 <parent> 12 <groupId>org.springframework.boot</groupId> 13 <artifactId>spring-boot-starter-parent</artifactId> 14 <version>2.0.4.RELEASE</version> 15 </parent> 16 17 <dependencies> 18 <!-- web --> 19 <dependency> 20 <groupId>org.springframework.boot</groupId> 21 <artifactId>spring-boot-starter-web</artifactId> 22 </dependency> 23 <!-- thymeleaf --> 24 <dependency> 25 <groupId>org.springframework.boot</groupId> 26 <artifactId>spring-boot-starter-thymeleaf</artifactId> 27 </dependency> 28 <!-- spring data jpa --> 29 <dependency> 30 <groupId>org.springframework.boot</groupId> 31 <artifactId>spring-boot-starter-data-jpa</artifactId> 32 </dependency> 33 <!-- mariadb --> 34 <dependency> 35 <groupId>org.mariadb.jdbc</groupId> 36 <artifactId>mariadb-java-client</artifactId> 37 <version>2.2.5</version> 38 </dependency> 39 <!-- shiro --> 40 <dependency> 41 <groupId>org.apache.shiro</groupId> 42 <artifactId>shiro-spring</artifactId> 43 <version>1.4.0</version> 44 </dependency> 45 <!-- 热启动 --> 46 <dependency> 47 <groupId>org.springframework.boot</groupId> 48 <artifactId>spring-boot-devtools</artifactId> 49 <optional>true</optional> 50 </dependency> 51 </dependencies> 52 </project>
2、编写项目配置文件application.properties
1 # 数据库访问配置 2 # 对应MariaDB驱动 3 spring.datasource.driverClassName=org.mariadb.jdbc.Driver 4 # 数据源配置 5 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test 6 spring.datasource.username=root 7 spring.datasource.password=sa 8 # 配置Springboot默认支持的Hikari数据库连接池 9 spring.datasource.type=com.zaxxer.hikari.HikariDataSource 10 spring.datasource.hikari.minimum-idle=5 11 spring.datasource.hikari.maximum-pool-size=15 12 spring.datasource.hikari.auto-commit=true 13 spring.datasource.hikari.idle-timeout=30000 14 spring.datasource.hikari.pool-name=DatebookHikariCP 15 spring.datasource.hikari.max-lifetime=1800000 16 spring.datasource.hikari.connection-timeout=30000 17 spring.datasource.hikari.connection-test-query=SELECT 1 18 # Spring Data JPA配置 19 spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect 20 spring.jpa.properties.hibernate.hbm2ddl.auto=update 21 spring.jpa.show-sql=true 22 spring.jpa.properties.hibernate.format_sql=true 23 # 格式化输出的json字符串 24 spring.jackson.serialization.indent_output=true 25 # 设置控制台彩色打印 26 spring.output.ansi.enabled=ALWAYS
3、编写项目启动类Application.java
1 package cn.temptation;
2
3 import org.springframework.boot.SpringApplication;
4 import org.springframework.boot.autoconfigure.SpringBootApplication;
5
6 @SpringBootApplication
7 public class Application {
8 public static void main(String[] args) {
9 // SpringBoot项目启动
10 SpringApplication.run(Application.class, args);
11 }
12 }
4、编写登录页面login.html 和 首页页面index.html
登录页面:login.html
1 <!DOCTYPE html>
2 <html xmlns:th="http://www.thymeleaf.org">
3 <head>
4 <meta charset="UTF-8">
5 <title>系统登录</title>
6 </head>
7 <body>
8 <div th:text="${msg}" style="color: red"></div>
9 <form action="doLogin" method="post">
10 帐号:<input type="text" id="txtUsername" name="username" /><br/>
11 密码:<input type="password" id="txtPassword" name="password" /><br/><br/>
12 <input type="submit" value="提交" /> <input type="reset" value="重置" />
13 </form>
14 </body>
15 </html>
首页页面:index.html
1 <!DOCTYPE html>
2 <html xmlns:th="http://www.thymeleaf.org">
3 <head>
4 <meta charset="UTF-8">
5 <title>系统首页</title>
6 </head>
7 <body>
8 <div th:text="${'欢迎您,' + currentuser}" style="color: red;float: left;"></div>
9 <div style="color: red;float: right;"><a href="doLogout">注销</a></div>
10 </body>
11 </html>
5、编写Shiro框架用配置类ShiroConfig.java 和 自定义Realm类MyRealm.java
配置类ShiroConfig.java
1 package cn.temptation.shiro;
2
3 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
4 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
5 import org.springframework.beans.factory.annotation.Qualifier;
6 import org.springframework.context.annotation.Bean;
7 import org.springframework.context.annotation.Configuration;
8
9 import java.util.LinkedHashMap;
10 import java.util.Map;
11
12 /**
13 * Shiro配置类
14 */
15 @Configuration
16 public class ShiroConfig {
17 // 1、创建ShiroFilterFactoryBean
18 @Bean
19 public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
20 ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
21 // 设置安全管理器
22 shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
23
24 // 设置登录跳转页面
25 shiroFilterFactoryBean.setLoginUrl("/login");
26
27 /**
28 * Shiro内置过滤器:实现权限相关的拦截
29 * 常用过滤器:
30 * anon(认证用):无需认证(登录)即可访问
31 * authc(认证用):必须认证才可访问
32 * user(少用):使用rememberMe功能可以访问
33 * perms(授权用):必须得到资源权限才可访问
34 * role(授权用):必须得到角色权限才可访问
35 */
36 Map<String, String> filterMap = new LinkedHashMap<>();
37
38 // 放行登录请求
39 filterMap.put("/doLogin", "anon");
40
41 // 配置退出过滤器,退出代码Shiro已经实现
42 filterMap.put("/logout", "logout");
43
44 // 过滤链定义,从上向下顺序执行,一般将/*放在最下边
45 filterMap.put("/*", "authc");
46
47 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
48
49 return shiroFilterFactoryBean;
50 }
51
52 // 2、创建DefaultWebSecurityManager
53 @Bean(name = "securityManager")
54 public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("myRealm") MyRealm myRealm) {
55 DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
56
57 // 关联Realm
58 defaultWebSecurityManager.setRealm(myRealm);
59
60 return defaultWebSecurityManager;
61 }
62
63 // 3、创建Realm
64 @Bean(name = "myRealm")
65 public MyRealm getRealm() {
66 return new MyRealm();
67 }
68 }
自定义Realm类MyRealm.java
1 package cn.temptation.shiro;
2
3 import cn.temptation.dao.UserDao;
4 import cn.temptation.domain.User;
5 import org.apache.shiro.authc.*;
6 import org.apache.shiro.authz.AuthorizationInfo;
7 import org.apache.shiro.realm.AuthorizingRealm;
8 import org.apache.shiro.subject.PrincipalCollection;
9 import org.springframework.beans.factory.annotation.Autowired;
10
11 /**
12 * 自定义Realm
13 */
14 public class MyRealm extends AuthorizingRealm {
15 @Autowired
16 private UserDao userDao;
17
18 // 授权处理
19 @Override
20 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
21 return null;
22 }
23
24 // 认证处理
25 @Override
26 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
27 // 编写Shiro判断逻辑,判断账号和密码
28 // 1、判断账号
29 UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
30
31 User user = userDao.findByUsername(token.getUsername());
32 if (user == null) {
33 // 账号错误,Shiro底层会抛出UnknownAccountException异常
34 return null;
35 }
36
37 // 2、判断密码
38 return new SimpleAuthenticationInfo("", user.getPassword(), "");
39 }
40 }
6、编写实体类User.java
1 package cn.temptation.domain;
2
3 import javax.persistence.*;
4
5 @Entity
6 @Table(name = "sys_user")
7 public class User {
8 @Id
9 @GeneratedValue(strategy = GenerationType.IDENTITY)
10 @Column(name = "userid")
11 private Integer userid;
12
13 @Column(name = "username")
14 private String username;
15
16 @Column(name = "password")
17 private String password;
18
19 public Integer getUserid() {
20 return userid;
21 }
22
23 public void setUserid(Integer userid) {
24 this.userid = userid;
25 }
26
27 public String getUsername() {
28 return username;
29 }
30
31 public void setUsername(String username) {
32 this.username = username;
33 }
34
35 public String getPassword() {
36 return password;
37 }
38
39 public void setPassword(String password) {
40 this.password = password;
41 }
42 }
7、编写控制器类UserController.java
1 package cn.temptation.web;
2
3 import org.apache.shiro.SecurityUtils;
4 import org.apache.shiro.authc.IncorrectCredentialsException;
5 import org.apache.shiro.authc.UnknownAccountException;
6 import org.apache.shiro.authc.UsernamePasswordToken;
7 import org.apache.shiro.subject.Subject;
8 import org.springframework.stereotype.Controller;
9 import org.springframework.ui.Model;
10 import org.springframework.web.bind.annotation.RequestMapping;
11
12 @Controller
13 public class UserController {
14 // 访问登录页
15 @RequestMapping("/login")
16 public String login() {
17 return "login";
18 }
19
20 // 访问首页
21 @RequestMapping("/index")
22 public String index() {
23 return "index";
24 }
25
26 // 登录处理
27 @RequestMapping("/doLogin")
28 public String doLogin(String username, String password, Model model) {
29 // 使用Shiro编写认证处理
30 // 1、获取Subject
31 Subject subject = SecurityUtils.getSubject();
32
33 // 2、封装用户数据
34 UsernamePasswordToken token = new UsernamePasswordToken(username, password);
35
36 // 3、执行登录
37 try {
38 // 登录成功
39 subject.login(token);
40
41 // 返回当前用户的帐号
42 model.addAttribute("currentuser", token.getUsername());
43
44 return "index";
45 } catch (UnknownAccountException exception) {
46 // 返回错误信息
47 model.addAttribute("msg", "账号错误!");
48
49 return "login";
50 } catch (IncorrectCredentialsException exception) {
51 // 返回错误信息
52 model.addAttribute("msg", "密码错误!");
53
54 return "login";
55 }
56 }
57
58 // 注销处理
59 @RequestMapping("/doLogout")
60 public String doLogout() {
61 // 1、获取Subject
62 Subject subject = SecurityUtils.getSubject();
63
64 // 2、执行注销
65 try {
66 subject.logout();
67 } catch (Exception ex) {
68 ex.printStackTrace();
69 } finally {
70 return "login";
71 }
72 }
73 }
8、编写数据访问接口UserDao.java
1 package cn.temptation.dao;
2
3 import cn.temptation.domain.User;
4 import org.springframework.data.jpa.repository.JpaRepository;
5 import org.springframework.data.jpa.repository.Query;
6 import org.springframework.data.repository.query.Param;
7
8 public interface UserDao extends JpaRepository<User, Integer> {
9 // 根据账号查询用户
10 @Query(value = "SELECT * FROM sys_user WHERE username=:username", nativeQuery = true)
11 User findByUsername(@Param("username") String username);
12 }
9、项目结构
10、运行效果
欢迎工作一到八年的Java工程师朋友们加入Java高级交流群:854630135
本群提供免费的学习指导 架构资料 以及免费的解答
不懂得问题都可以在本群提出来 之后还会有直播平台和讲师直接交流噢
哦对了,喜欢就别忘了关注一下哦~

154

被折叠的 条评论
为什么被折叠?



