SpringBoot 企业级简化开发(三)https://developer.aliyun.com/article/1469562
配置druid步骤
- 导入相关依赖,
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.6</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
- 配置对应的数据源
- 对应配置文件
spring: datasource: driver-class-name: com.mysql.jdbc.Driver password: xxx username: xxx url: jdbc:mysql://localhost:3306/mybatis #?serverTimezone=UTC&useUnicode=true&charcterEncoding=UTF-8 type: com.alibaba.druid.pool.DruidDataSource
- 去将我们的增删改查方法测试一下就好了,用的是jdbc模版,由boot提供:jdbcTemplate
package com.hyc.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.Map; @RestController public class Datacontroller { @Autowired JdbcTemplate jdbcTemplate; @RequestMapping("/userlist") public List<Map<String,Object>> userlist(){ String sql = "select * from mybatis.user"; List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql); return maps; } @RequestMapping("/addUser") public String addUser(){ String sql = "insert into mybatis.user(id,name,pwd) values(1,'小胡','123456')"; jdbcTemplate.update(sql); return "insert"; } @RequestMapping("/upddateUser/{id}") public String upddateUser(@PathVariable("id") int id){ String sql = "update mybatis.user set name=?,pwd=? where id ="+id; Object[] obj = new Object[2]; obj[0] = "小明"; obj[1] = "66666"; jdbcTemplate.update(sql,obj); return "update"; } @RequestMapping("/DeleteUser/{id}") public String DeleteUser(@PathVariable("id") int id){ String sql = "delete from mybatis.user where id=? "; jdbcTemplate.update(sql,id); return "DeleteUser"; } }
- YML配置druid常用的一些配置
#SpringBoot默认是不注入这些的,需要自己绑定 #druid数据源专有配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入 #如果允许报错,java.lang.ClassNotFoundException: org.apache.Log4j.Properity #则导入log4j 依赖就行 filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
- 配置sql监控,和过滤请求:这里注意不要私自改动:loginusername等key
package com.hyc.config; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.Filter; import javax.servlet.ServletRegistration; import javax.sql.DataSource; import java.util.HashMap; @Configuration public class DruidConfing { @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); } //后台监控 @Bean public ServletRegistrationBean ServletRegistrationBean(){ ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*"); HashMap<String, String> InitParameters = new HashMap<>(); InitParameters.put("loginUsername","admin"); InitParameters.put("loginPassword","123456"); InitParameters.put("allow",""); //后台监控 bean.setInitParameters(InitParameters); return bean; } @Bean public FilterRegistrationBean filterRegistrationBean(){ FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>(); //可以过滤那些请求? bean.setFilter(new WebStatFilter()); bean.addUrlPatterns("/*"); bean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); return bean; } }
SpringBoot整合mybatis
准备工作,
寻找依赖,添加,这是个第三方的启动器
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency>
去创建使用mybatis的需要文件,
实体类:user
mapper接口:usermapper
配置文件:usermapper.xml
创建完毕后我们去boot的配置文件配置mybatis:
#mybatis配置 mybatis.mapper-location=classpath:mapper/*.xml mybatis.type-aliases-package=com.hyc.pojo
配置文件模板:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace 写入 自己 dao 里的接口名称--> <mapper namespace="com.hyc.mapper.userMapper"> </mapper>
用mybatis的注解来 映射,实体类:
package com.hyc.mapper; import com.hyc.pojo.User; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Repository; import java.util.List; @Mapper @Repository public interface userMapper { List<User> getuser(); User getuserByid(int id); int addUser(User user); int UpdUser(User user); int DelUser(int id); }
之后编写映射文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace 写入 自己 dao 里的接口名称--> <mapper namespace="com.hyc.mapper.userMapper"> <insert id="addUser"> insert into mybatis.user(id, name, pwd) VALUES (#{id},#{name},#{pwd}); </insert> <update id="UpdUser"> update mybatis.user set user.name = #{name},user.pwd =#{pwd} where id = #{id}; </update> <delete id="DelUser"> delete from mybatis.user where id=#{id} </delete> <select id="getuser" resultType="com.hyc.pojo.User"> select * from mybatis.user </select> <select id="getuserByid" resultType="com.hyc.pojo.User"> select * from mybatis.user where id=#{id}; </select> </mapper>
编写控制层测试即可:
@RestController public class usercontroller { @Autowired private userMapper userMapper; @RequestMapping("/queryList") public List<User> queryList(){ List<User> getuser = userMapper.getuser(); for (User user : getuser) { System.out.println(user); } return getuser; } }
SpringSecurity(安全)
Springsecurity简介
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
web开发中 ,安全是第一位的,我们学过的有,过滤器,拦截器
spring security 的核心功能主要包括:
- 认证 (你是谁)
- 授权 (你能干什么)
- 攻击防护 (防止伪造身份)
功能性需求:否
做网站需要考虑什么?
- 漏洞,隐私泄露问题
- 架构一旦确定,再加入安全十分浪费人力,
目前两大火热的安全框架:SpringSecurity和shrio,他们两个十分相似
- 功能权限
- 访问权限
- 菜单权限
- 以前我们都是用拦截器,过滤器,来做,这样会有一个缺点,大量的原生代码,冗余
其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic Authentication Filter 用来认证用户的身份,一个在spring security中一种过滤器处理一种认证方式。
记住几个类:
- webSecurityConfigurerAdapter:自定义security策略
- AuthenticationManagerBuilder:自定义认证策略
- @EnablewebSecurity:开启webSercurity
官网地址:https://spring.io/projects/spring-security
帮助文档:https://docs.spring.io/spring-security/site/docs/current/reference/html5/
Security实战
导入相关依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
一般我们,配置Security的配置类都有一个模式
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); } }
接下来就可以来制定我们的需求了
需求1:首页所有人可以访问,功能页只有对应的权限才能进去
编写:SecurityConfig 类
@Override protected void configure(HttpSecurity http) throws Exception { //首页所有人可以访问,功能页只有对应的权限才能进去 http.authorizeRequests()//授权请求方法 .antMatchers("/").permitAll() .antMatchers("views/level1/*").hasRole("vip1")//什么角色可以访问什么位置 .antMatchers("views/level2/*").hasRole("vip2") .antMatchers("views/level3/*").hasRole("vip3"); }
给不同的用户,添加访问权限,
PS:在5.x之后密码要加密才可以有效: 使用以这个为后缀的类passwordEncoder
//密码编码:passwordencoder //5.x之后更新了很多的加密方法 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) .withUser("hyc").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3") .and() .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3") .and() .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1"); }
设置注销:
http.logout().logoutSuccessUrl("/index");
thymeleaf 常用命名空间:
xmlns:th=http://www.thymeleaf.org xmlns:sec=http://www.thymeleaf.org/extras/spring-security xmlns:shiro=http://www.pollix.at/thymeleaf/shiro html lang=en xmlns:th=http://www.thymeleaf.org xmlns:sec=http://www.thymeleaf.org/extras/spring-security xmlns:shiro=http://www.pollix.at/thymeleaf/shiro
常用的内容:
PS:如果403:csrf().disable();
关闭跨站攻击安全
匹配的路径不需要认证:.antMatchers("/","/test/hello","/user/login").permitAll()
按照权限显示:sec:authorize="hasRole("xxx")
是否登陆显示:sec:authorize="!isAuthenticated()"
指定登陆的页面:.loginPage("/toLogin")
登陆访问路径:提交表单之后跳转的地址,可以看作一个中转站,这个步骤就是验证user的一个过程:.loginProcessingUrl("/login");
注销之后转发的页面:.logoutSuccessUrl("/index");
记住我:
//开启记住我功能 cookie 默认保存两周 http.rememberMe();
自定义用户参数和密码参数
//.usernameParameter() //.passwordParameter()
shiro(安全)
阿帕奇的安全框架
Apache Shiro是一个Java的安全管理框架,可以用在JavaEE环境下,也可以用在JavaSE环境下。
此前我们学习了很多有关阿帕奇的东西:maven,tomcat,等等
官方号称十分钟就可以入门,
为什么学他?:
(1)spring security 功能完善,学习成本偏高;
(2)shiro 学习成本低,简单的安全框架,基本功能存在(登录认证,权限认证);
(3)spring mvc interceptor(拦截器) 只能做登录认证,不能做权限认证。
他能做什么?
Authentication:身份认证/登录;
Authorization:授权;
Session Manager:会话管理;
Cryptography:加密;
Web Support:**Web支持,可以非常容易的集成到Web环境;
Caching:缓存;
Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我。
Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro。
shiro架构
Subject:主体;
SecurityManager:相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。
Authenticator:认证器;
Authrizer:授权器,;
Realm:可以有1个或多个Realm,是安全实体数据源;可以是JDBC实现,也可以是LDAP实现,或者内存实现等等;
SessionManager:**Shiro自己的Session来管理主体与应用之间交互的数据;
SessionDAO:**DAO大家都用过,数据访问对象,用于会话的CRUD;同时SessionDao也可以使用Cache进行缓存以提高性能。
CacheManager:缓存控制器,管理如用户、角色、权限等的缓存
Cryptography:密码模块
helloshiro
导入相关依赖:官方依赖
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.6.0</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
<resources> <resource> <directory>${basedir}/src/main/webapp</directory> </resource> <resource> <directory>${basedir}/src/main/resources</directory> </resource> <resource> <directory>${basedir}/src/main/java</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> <include>**/*.yml</include> </includes> </resource> </resources>
修改后的Quickstart类,
最新版本建议将:
FactorySecurityManager factory = new IniSecurityManagerFactory(classpath:shiro.ini); SecurityManager securityManager = factory.getInstance(); 转换位下面这样 DefaultSecurityManager securityManager = new DefaultSecurityManager(); IniRealm iniRealm = new IniRealm(classpath:shiro.ini); securityManager.setRealm(iniRealm);
下面代码是Quickstart类:
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.realm.text.IniRealm; import org.apache.shiro.session.Session; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Simple Quickstart application showing how to use Shiro's API. * * @since 0.9 RC2 */ public class Quickstart { private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class); public static void main(String[] args) { // The easiest way to create a Shiro SecurityManager with configured // realms, users, roles and permissions is to use the simple INI config. // We'll do that by using a factory that can ingest a .ini file and // return a SecurityManager instance: // Use the shiro.ini file at the root of the classpath // (file: and url: prefixes load from files and urls respectively): DefaultSecurityManager securityManager = new DefaultSecurityManager(); IniRealm iniRealm = new IniRealm("classpath:shiro.ini"); securityManager.setRealm(iniRealm); // for this simple example quickstart, make the SecurityManager // accessible as a JVM singleton. Most applications wouldn't do this // and instead rely on their container configuration or web.xml for // webapps. That is outside the scope of this simple quickstart, so // we'll just do the bare minimum so you can continue to get a feel // for things. SecurityUtils.setSecurityManager(securityManager); // Now that a simple Shiro environment is set up, let's see what you can do: // get the currently executing user: Subject currentUser = SecurityUtils.getSubject(); // Do some stuff with a Session (no need for a web or EJB container!!!) Session session = currentUser.getSession(); session.setAttribute("someKey", "aValue"); String value = (String) session.getAttribute("someKey"); if (value.equals("aValue")) { log.info("Retrieved the correct value! [" + value + "]"); } // let's login the current user so we can check against roles and permissions: if (!currentUser.isAuthenticated()) { UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa"); token.setRememberMe(true); try { currentUser.login(token); } catch (UnknownAccountException uae) { log.info("There is no user with username of " + token.getPrincipal()); } catch (IncorrectCredentialsException ice) { log.info("Password for account " + token.getPrincipal() + " was incorrect!"); } catch (LockedAccountException lae) { log.info("The account for username " + token.getPrincipal() + " is locked. " + "Please contact your administrator to unlock it."); } // ... catch more exceptions here (maybe custom ones specific to your application? catch (AuthenticationException ae) { //unexpected condition? error? } } //say who they are: //print their identifying principal (in this case, a username): log.info("User [" + currentUser.getPrincipal() + "] logged in successfully."); //test a role: if (currentUser.hasRole("schwartz")) { log.info("May the Schwartz be with you!"); } else { log.info("Hello, mere mortal."); } //test a typed permission (not instance-level) if (currentUser.isPermitted("lightsaber:wield")) { log.info("You may use a lightsaber ring. Use it wisely."); } else { log.info("Sorry, lightsaber rings are for schwartz masters only."); } //a (very powerful) Instance Level permission: if (currentUser.isPermitted("winnebago:drive:eagle5")) { log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " + "Here are the keys - have fun!"); } else { log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!"); } //all done - log out! currentUser.logout(); System.exit(0); } }
三大对象:
- Subject
- SecurityManage
- fealm
shiro整个流程
常用过滤器如下
- anon:无需认证访问
- authc:必须认证了才能访问
- user:记住我开启了,才可以用
- perms:拥有对某个资源的权限才能访问
- role:该资源必须得到角色权限才可以访问
代码实战:
首先是shiroconfig,我们从下往上配置:
- 首先是创建一个realm类
- 之后是创建shiroconfig
- 之后从下往上配置
- 首先是引入realm类
- 配置安全管理器
- 之后设置过滤工厂
package com.hyc.config; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; 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; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.Map; @Configuration public class shrioconfig { // shirofilterfactoryBean @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); //设置安全管理器 bean.setSecurityManager(defaultWebSecurityManager); /* * 常用过滤器如下 * anon:无需认证访问 * authc:必须认证了才能访问 * user:记住我开启了,才可以用 * perms:拥有对某个资源的权限才能访问 * */ * */ Map<String,String> filter = new LinkedHashMap(); filter.put("/add","anon"); filter.put("/upd","authc"); bean.setFilterChainDefinitionMap(filter); bean.setLoginUrl("/tologin"); return bean; } // dafultwebSecurityManager @Bean(name="SecurityManager") public DefaultWebSecurityManager getdefaultWebSecurityManager(@Qualifier("Userrealm") userrealm userrealm){ DefaultWebSecurityManager SecurityManager = new DefaultWebSecurityManager(); // 关联Userrealm SecurityManager.setRealm(userrealm); return SecurityManager; } // 创建realm对象,需要自定义类 @Bean public userrealm Userrealm() { return new userrealm(); } }
realm对象需要引用外面的类 userrealm,我们需要继承AuthorizingRealm来获得授权,认证方法
package com.hyc.config; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; public class userrealm extends AuthorizingRealm { //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("授权=========>"); return null; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("认证=========>"); return null; } }
用户认证:
我们需要去编写config类,设置权限,什么路径需要什么权限,
@Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); //设置安全管理器 bean.setSecurityManager(defaultWebSecurityManager); /* * 常用过滤器如下 * anon:无需认证访问 * authc:必须认证了才能访问 * user:记住我开启了,才可以用 * perms:拥有对某个资源的权限才能访问 role:该资源必须得到角色权限才可以访问 * */ Map<String,String> filter = new LinkedHashMap(); filter.put("/user/add","perms[user:add]"); filter.put("/user/upd","perms[user:upd]"); bean.setFilterChainDefinitionMap(filter); bean.setLoginUrl("/tologin"); bean.setUnauthorizedUrl("/unauth"); return bean; }
之后去realm去认证,认证的信息是我们从数据库user表中查询出来的数据
//认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("认证=========>"); // 获取当前的用户 Subject subject = SecurityUtils.getSubject(); // 封装用户数据 UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; user user = userService.queryUserByName(token.getUsername()); if (user==null){ return null; } //认证的时候创建用户登陆的session Session session = subject.getSession(); //将用户的属性传入到session中 session.setAttribute("loginUser",user); //如何让我们的user可以全局使用,我们需要设置info中第一个参数为user return new SimpleAuthenticationInfo(user,user.getPassword() ,""); }
之后再认证之后获取用户对象授权,什么对象可以访问什么页面
//授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("授权=========>"); //授权信息 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //传递用户信息 Subject subject = SecurityUtils.getSubject(); user currentUser = (user) subject.getPrincipal(); //从数据库中获取授权角色 info.addStringPermission(currentUser.getParms()); info.addRole("user:add"); info.addRole("user:upd"); return info; }
登陆功能
controller
@RequestMapping("/login") public String login(String username,String password,Model model){ //获取角色对象 Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username,password); try { subject.login(token);//判断令牌是否正确 return "index"; } catch (UnknownAccountException uae) {//用户名不存在 model.addAttribute("msg","用户名不存在"); return "login"; } catch (IncorrectCredentialsException ice) {//密码不存在 model.addAttribute("msg","密码错误"); return "login"; } }
这里有一个拓展可以做:就是密码加密处理
我们这里调用的login()方法会走上面我们配置的一系列流程
整合shiro和thymeleaf
需要的命名空间
xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"
要使用整合我们还需要导入整合包依赖
<!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro --> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency>
之后需要去配置类配置一个新的bean
//整合ShiroDialect:用来整合shiro thymeleaf @Bean public ShiroDialect getshiroDialect(){ return new ShiroDialect(); }
完成以上步骤就可以在模版引擎上使用shiro了
前端页面内容
<!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> <span th:text="${msg}"></span> <!--从session中判断值--> <div th:if="${session.get('loginUser')==null}"> <a th:href="@{/tologin}">登录</a> </div> <a th:href="@{/logout}">注销</a> <p th:text="${msg}"></p> <hr> <!--通过shiro中的hasPermission方法,判断登录的用户是否有这个权限,有权限才显示--> <div shiro:hasPermission="user:add"> <a th:href="@{/user/add}" >add</a> </div> <div shiro:hasPermission="user:upd"> <a th:href="@{/user/upd}">update</a> </div> </body> </html>
源码
配置相关
shiroconfig
package com.hyc.config; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; 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; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.Map; @Configuration public class shrioconfig { // shirofilterfactoryBean @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); //设置安全管理器 bean.setSecurityManager(defaultWebSecurityManager); /* * 常用过滤器如下 * anon:无需认证访问 * authc:必须认证了才能访问 * user:记住我开启了,才可以用 * perms:拥有对某个资源的权限才能访问 * */ Map<String,String> filter = new LinkedHashMap(); filter.put("/user/add","perms[user:add]"); filter.put("/user/upd","perms[user:upd]"); bean.setFilterChainDefinitionMap(filter); bean.setLoginUrl("/tologin"); bean.setUnauthorizedUrl("/unauth"); return bean; } // dafultwebSecurityManager @Bean(name="SecurityManager") public DefaultWebSecurityManager getdefaultWebSecurityManager(@Qualifier("Userrealm") userrealm userrealm){ DefaultWebSecurityManager SecurityManager = new DefaultWebSecurityManager(); // 关联Userrealm SecurityManager.setRealm(userrealm); return SecurityManager; } // 创建realm对象,需要自定义类 @Bean public userrealm Userrealm() { return new userrealm(); } //整合ShiroDialect:用来整合shiro thymeleaf @Bean public ShiroDialect getshiroDialect(){ return new ShiroDialect(); } }
userrealm
package com.hyc.config; import com.hyc.pojo.user; import com.hyc.service.userServiceImpl; 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.session.Session; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; public class userrealm extends AuthorizingRealm { @Autowired userServiceImpl userService; //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("授权=========>"); //授权信息 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //传递用户信息 Subject subject = SecurityUtils.getSubject(); user currentUser = (user) subject.getPrincipal(); //授权角色 info.addStringPermission(currentUser.getParms()); info.addRole("user:add"); info.addRole("user:upd"); return info; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("认证=========>"); // 获取当前的用户 Subject subject = SecurityUtils.getSubject(); // 封装用户数据 UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; user user = userService.queryUserByName(token.getUsername()); if (user==null){ return null; } Session session = subject.getSession(); session.setAttribute("loginUser",user); return new SimpleAuthenticationInfo(user,user.getPassword() ,""); } }
控制层
package com.hyc.controller; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.session.ProxiedSession; import org.apache.shiro.session.Session; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @Controller public class Mycontroller { @RequestMapping({"/","/index"}) public String toIndex(Model model){ model.addAttribute("msg","hello"); return "index"; } @RequestMapping("/user/add") public String add(){ return "user/add"; } @RequestMapping("/user/upd") public String upd(){ return "user/upd"; } @RequestMapping("/tologin") public String tologin(){ return "login"; } @RequestMapping("/login") public String login(String username,String password,Model model){ Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username,password); try { subject.login(token); return "index"; } catch (UnknownAccountException uae) {//用户名不存在 model.addAttribute("msg","用户名不存在"); return "login"; } catch (IncorrectCredentialsException ice) {//密码不存在 model.addAttribute("msg","密码错误"); return "login"; } } @ResponseBody @RequestMapping("/unauth") public String unauth(){ return "您没有权限"; } @RequestMapping("/logout") public String logout(){ Subject subject = SecurityUtils.getSubject(); Session session = subject.getSession(); session.removeAttribute("loginUser"); return "index"; } }
前端页面:
index
<!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> <span th:text="${msg}"></span> <!--从session中判断值--> <div th:if="${session.get('loginUser')==null}"> <a th:href="@{/tologin}">登录</a> </div> <a th:href="@{/logout}">注销</a> <p th:text="${msg}"></p> <hr> <div shiro:hasPermission="user:add"> <a th:href="@{/user/add}" >add</a> </div> <div shiro:hasPermission="user:upd"> <a th:href="@{/user/upd}">update</a> </div> </body> </html>
login
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <p>登陆</p> <p th:text="${msg}" style="color: red"></p> <form th:action="@{/login}"> <p> 用户名:<input type="text" name="username"></p> <p> 密码:<input type="password" name="password"></p> <p><input type="submit" value="提交"></p> </form> </body> </html>
SpringBoot 企业级简化开发(五)https://developer.aliyun.com/article/1469564