开发环境搭建参见《》
需求:
① 除了登录页面,在地址栏直接访问其他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 25 4.0.0 6 7cn.temptation 8studyShiro 91.0-SNAPSHOT 10 1112 16 17org.springframework.boot 13spring-boot-starter-parent 142.0.4.RELEASE 1518 19 5220 23 24org.springframework.boot 21spring-boot-starter-web 2225 28 29org.springframework.boot 26spring-boot-starter-thymeleaf 2730 33 34org.springframework.boot 31spring-boot-starter-data-jpa 3235 39 40org.mariadb.jdbc 36mariadb-java-client 372.2.5 3841 45 46org.apache.shiro 42shiro-spring 431.4.0 4447 51org.springframework.boot 48spring-boot-devtools 49true 50
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.HikariDataSource10 spring.datasource.hikari.minimum-idle=511 spring.datasource.hikari.maximum-pool-size=1512 spring.datasource.hikari.auto-commit=true13 spring.datasource.hikari.idle-timeout=3000014 spring.datasource.hikari.pool-name=DatebookHikariCP15 spring.datasource.hikari.max-lifetime=180000016 spring.datasource.hikari.connection-timeout=3000017 spring.datasource.hikari.connection-test-query=SELECT 118 # Spring Data JPA配置19 spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect20 spring.jpa.properties.hibernate.hbm2ddl.auto=update21 spring.jpa.show-sql=true22 spring.jpa.properties.hibernate.format_sql=true23 # 格式化输出的json字符串24 spring.jackson.serialization.indent_output=true25 # 设置控制台彩色打印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 2 3 4 514 15系统登录 6 7 8 9
首页页面:index.html
1 2 3 4 5系统首页 6 7 8 9 10 11
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 @Configuration16 public class ShiroConfig {17 // 1、创建ShiroFilterFactoryBean18 @Bean19 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 MapfilterMap = 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、创建DefaultWebSecurityManager53 @Bean(name = "securityManager")54 public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("myRealm") MyRealm myRealm) {55 DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();56 57 // 关联Realm58 defaultWebSecurityManager.setRealm(myRealm);59 60 return defaultWebSecurityManager;61 }62 63 // 3、创建Realm64 @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 * 自定义Realm13 */14 public class MyRealm extends AuthorizingRealm {15 @Autowired16 private UserDao userDao;17 18 // 授权处理19 @Override20 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {21 return null;22 }23 24 // 认证处理25 @Override26 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 @Controller13 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、获取Subject31 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、获取Subject62 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{ 9 // 根据账号查询用户10 @Query(value = "SELECT * FROM sys_user WHERE username=:username", nativeQuery = true)11 User findByUsername(@Param("username") String username);12 }
9、项目结构
10、运行效果