SpringSecruity
SpringBoot的默认安全技术模块
1、需要引入的starter
spring-boot-starter-security
2、关联到的类
WebSecurityConfigurationAdapter: 【适配器模式】自定义Security策略
AuthenticationManagerBuilder【建造者模式】自定义认证策略
@EnableWebSecurity 开启WebSecurity模式
3、扩展配置类
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 认证(为登录用户增加特权)
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("kuangshen").password("123456").roles("vip2","vip3")
.and()
.withUser("root").password("root").roles("vip1","vip2","vip3");
}
// 授权(在资源上增加权限认证)
// 链式编程
@Override
protected void configure(HttpSecurity http) throws Exception {
//首页所有人可以访问,但是里面的功能也只能对应有权限的人才能访问
//请求授权的规则,antMatchers( 拦截的url),后面参数为权限要求
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasAnyRole("vip1")
.antMatchers("/level2/**").hasAnyRole("vip2")
.antMatchers("/level3/**").hasAnyRole("vip3");
//权限失败,默认到登录页面,需要开启登录的页面
//添加一个security自带的权限登录功能,加入/login映射以及对应功能
http.formLogin()
.loginPage("自定义登录页控制器url")
.loginProcessingUrl("自定义登录POST验证控制器url")
.usernameParameter("自定义用户名接收参数名")
.passwordParameter("自定义密码接收参数名");
//是否限制logout 只能用post方式请求
http.csrf().disable();
//添加一个security自带的注销功能,加入/logout映射以及对应功能
http.logout();
//记住我功能 使用cookie 默认保存2周
http.rememberMe()
.rememberMeParameter("自定义记录登录状态参数名");
}
}
4、页面通过标签表达式完成指定验证
a、这里需要先导入Thymeleaf Security 的jar包
b、再在页面声明使用这个
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>首页</title>
<!--semantic-ui-->
<link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
<link th:href="@{/qinjiang/css/qinstyle.css}" rel="stylesheet">
</head>
<body>
<!--主容器-->
<div class="ui container">
<div class="ui segment" id="index-header-nav" th:fragment="nav-menu">
<div class="ui secondary menu">
<a class="item" th:href="@{/index}">首页</a>
<!--登录注销-->
<div class="right menu">
<!--未登录-->
<div sec:authorize="!isAuthenticated()">
<a class="item" th:href="@{/toLogin}">
<i class="address card icon"></i> 登录
</a>
</div>
<!--如果登陆-->
<div sec:authorize="isAuthenticated()">
<a class="item">
用户名:<span sec:authentication="name"></span>
角色:<span sec:authentication="principal.authorities"></span>
</a>
</div>
<div sec:authorize="isAuthenticated()">
<a class="item" th:href="@{/logout}">
<i class="sign-out icon"></i> 注销
</a>
</div>
<!--已登录
<a th:href="@{/usr/toUserCenter}">
<i class="address card icon"></i> admin
</a>
-->
</div>
</div>
</div>
<div class="ui segment" style="text-align: center">
<h3>Spring Security Study by 秦疆</h3>
</div>
<!--sec:authorize="hasRole('vip1')" 权限认证-->
<div>
<br>
<div class="ui three column stackable grid">
<div class="column" sec:authorize="hasRole('vip1')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 1</h5>
<hr>
<div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
<div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
<div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
</div>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip2')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 2</h5>
<hr>
<div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
<div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
<div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
</div>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip3')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 3</h5>
<hr>
<div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
<div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
<div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script th:src="@{/qinjiang/js/jquery-3.1.1.min.js}"></script>
<script th:src="@{/qinjiang/js/semantic.min.js}"></script>
</body>
</html>
Shiro
初识Shiro的Quickstart0
获取当前用户的对象 Subject
Subject currentUser = SecurityUtils.getSubject();
超过当前用户拿到Session,并处理Session(存执取值)
Session session = currentUser.getSession();
session.setAttribute( "someKey", "aValue" );
测试当前用户是否被认证 以及权限验证
if ( !currentUser.isAuthenticated() ) {
//collect user principals and credentials in a gui specific manner
//such as username/password html form, X509 certificate, OpenID, etc.
//We'll use the username/password example here since it is the most common.
//(do you know what movie this is from? ;)
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
//记住我
token.setRememberMe(true);
try {
//执行登录操作
currentUser.login( token );
//if no exception, that's it, we're done!
} catch ( UnknownAccountException uae ) {
//用户名不存在
} catch ( IncorrectCredentialsException ice ) {
//密码不匹配
} catch ( LockedAccountException lae ) {
//用户被锁定/限制/封禁
}
... more types exceptions to check if you want ...
} catch ( AuthenticationException ae ) {
//其他的整体错误(兜底)
}
//currentUser.getPrincipal() 获取用户登录取到的信息
log.info( "User [" + currentUser.getPrincipal() + "] logged in successfully." );
//hasRole当前用户拥有的角色
if ( currentUser.hasRole( "schwartz" ) ) {
log.info("May the Schwartz be with you!" );
} else {
log.info( "Hello, mere mortal." );
}
//粗粒度的权限,检查用户的大权限
if ( currentUser.isPermitted( "lightsaber:weild" ) ) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
//细粒度的权限,检查用户的细分权限
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!");
}
//用户注销功能
currentUser.logout();
//系统结束
System.exit(0);
}
Shiro需要创建的Realm对象类
功能:用来处理登录认证和授权相关操作
特点:看似与Shrio没有任何联系,实际上在login()等方法认证时都会调用这里
重点:需要继承AuthorizingRealm 并重写认证、授权方法
public class UserRealm extends AuthorizingRealm {
@Autowired
UserServcie userServcie;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了,授权行为");
//为用户授予权限-
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("user:add");
//动态赋予去权限
//获取当前用户
Subject subject = SecurityUtils.getSubject();
//返回值是Object,可以强转成认证时塞入的信息类型
User currentUser = (User) subject.getPrincipal();
//数据库中取出用户的权限
info.addStringPermission(currentUser.getPerms());
return info;
}
//认证 参数为login方法传入的token参数
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了,认证行为");
// 用户名、密码 数据库中取
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
User user = userServcie.queryUserByName(userToken.getUsername());
//用户名认证是自己认证
if(user == null){
return null;//这里返回null就直接进入到UnknownAccountException报错中
}
//加密, MD5加密和MD5盐值加密
//shiro内部进行密码认证
return new SimpleAuthenticationInfo("",user.getPwd(),"");
}
}
Shiro的Config配置类
重点:将3个BEAN放入IOC容器中,并用@Qualifier注解来【从上向下】调用
【拦截方法Bean工厂】getShiro(DefaultWebSecurityManager manager)
【默认的安全Manager】defaultWebSecurityManager(DefaultWebSecurityManager manager)
【创建Realm对象】userRealm()
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean 拦截器工厂Bean
@Bean
public ShiroFilterFactoryBean getShiro(@Qualifier("defaultWebSecurityManager")DefaultWebSecurityManager manager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(manager);//设置安全管理器
/**
* setFilterChainDefinitionMap 设置拦截方法
* anon:无需认证就可以访问
* authc:必须认证才能访问
* user: 必须拥有 记住我功能 才能访问
* perms:拥有对某个资源的权限时才能访问
* role:拥有某个角色权限才能访问
**/
Map<String, String> param = new LinkedHashMap<String,String>(){{
put("/user/add","anon");
put("/user/delete","perms[user:delete]");
}};
bean.setFilterChainDefinitionMap(param);
//设置跳转页面
// 设置登录的请求
bean.setLoginUrl("/登录页的url");
// 未授权页面
bean.setUnauthorizedUrl("/未授权提示页面");
return bean;
}
//DefaultWebSecurityManager 【Manager管理realm对象】
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userRealm);
return defaultWebSecurityManager;
}
// 创建realm对象,需要自定义类
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
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 e) {
model.addAttribute("msg","用户名异常");
e.printStackTrace();
return "/login";
}catch (IncorrectCredentialsException e){
model.addAttribute("msg","密码不匹配");
e.printStackTrace();
return "/login";
}
}
在Thymeleaf中整合Shiro
放在ShrioConfig里面就行
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}