SpringSecurity使用简介

简介:

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

相关概念:

主体(principal):

就是使用系统的用户,谁使用谁就是主体

认证(authentication):

就是登录操作,系统确认你的身份,允许你进入。

授权(authorization):

顾名思义,就是把权限授权给用户。

 

操作演示:

一 引入依赖:

<!-- SpringSecurity 对 Web 应用进行权限管理 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<!-- SpringSecurity 配置 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<!-- SpringSecurity 标签库 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>

二 加入SpringSecurity控制权限的Filter:

Filter和Interceptor的区别:

Filter是web中的三大组件之一,而Interceptor是SpringMVC中的拦截器链。

Filter是 所有的请求都进行拦截,包括静态资源。而Interceptor只拦截SpringMVC的handler请求。

它使用Filter也就意味着他不仅拦截handler请求,还拦截所有的web请求。

<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

注意:只能叫springSecurityFilterChain才能拦截到,这是固定的名称。

三 加入配置类

@Configuration
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
}

效果:

  • 所有请求都被SpringSecurity拦截,登录才可以访问
  • 登录失败有错误提示

四 操作

1.放行首页和静态资源

操作:

(1)在配置类中重写父类的 configure(HttpSecurity security)方法。

protected void configure(HttpSecurity security) throws Exception {
logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially
override subclass configure(HttpSecurity).");
security
.authorizeRequests()
.anyRequest().authenticated() //所有请求都需要进行认证
.and()
.formLogin()
.and()
.httpBasic();
}

重写后: 

@Override
protected void configure(HttpSecurity security) throws Exception {
//super.configure(security); 注释掉将取消父类方法中的默认规则
security.authorizeRequests() //对请求进行授权
.antMatchers("/layui/**","/index.jsp") //使用 ANT 风格设置要授权的 URL 地
址
.permitAll() //允许上面使用 ANT 风格设置的全部请求
.anyRequest() //其他未设置的全部请求
.authenticated(); //需要认证
}

(2).未认证请求跳转到登录页

protected void configure(HttpSecurity security) throws Exception {
//super.configure(security); 注释掉将取消父类方法中的默认规则
security.authorizeRequests() //对请求进行授权
.antMatchers("/layui/**","/index.jsp") //使用 ANT 风格设置要授权的 URL 地
址
.permitAll() //允许上面使用 ANT 风格设置的全部请求
.anyRequest() //其他未设置的全部请求
.authenticated() //需要认证
.and()
.formLogin() //设置未授权请求跳转到登录页面
.loginPage("/index.jsp") //指定登录页
.permitAll(); //为登录页设置所有人都可以访问
}

指定登陆地址:

security
.authorizeRequests() // 对请求进行授权
……
// loginProcessingUrl()方法指定了登录地址,就会覆盖 loginPage()方法中设置的默认值
/index.jsp POST
.loginProcessingUrl("/do/login.html") // 指定提交登录表单的地址
;

(3)设置登录的账号密码

给index.jsp设置表单,就是指定表单的提交地址和增加token
 

<p>${SPRING_SECURITY_LAST_EXCEPTION.message}</p>
<form action="${pageContext.request.contextPath }/do/login.html" method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> ……
</form>

配置账号密码

protected void configure(HttpSecurity security) throws Exception {
//super.configure(security); 注释掉将取消父类方法中的默认规则
security.authorizeRequests() //对请求进行授权
.antMatchers("/layui/**","/index.jsp") //使用 ANT 风格设置要授权的 URL 地址
.permitAll() //允许上面使用 ANT 风格设置的全部请求
.anyRequest() //其他未设置的全部请求
.authenticated() //需要认证
.and()
.formLogin() //设置未授权请求跳转到登录页面:开启表单登
录功能
.loginPage("/index.jsp") //指定登录页
.permitAll() //为登录页设置所有人都可以访问
.loginProcessingUrl("/do/login.html") // 指定提交登录表单的地址
.usernameParameter("loginAcct") // 定制登录账号的请求参数名
.passwordParameter("userPswd") // 定制登录密码的请求参数名
.defaultSuccessUrl("/main.html"); //设置登录成功后默认前往的 URL 地址

}

重写另外一个父类的方法,来设置登录系统的账号密码

protected void configure(AuthenticationManagerBuilder builder) throws Exception {
//super.configure(auth); 一定要禁用默认规则
builder.inMemoryAuthentication()
.withUser("tom").password("123123") //设置账号密码
.roles("ADMIN") //设置角色
.and()
.withUser("jerry").password("456456")//设置另一个账号密码
.authorities("SAVE","EDIT"); //设置权限
}

(4)用户注销

logout()方法:开启注销功能

logoutUrl()方法:自定义注销功能的 URL 地址

如果 CSRF 功能没有禁用,那么退出请求必须是 POST 方式。如果禁用了 CSRF 功能则任何请求方式都可以。

logoutSuccessUrl()方法:退出成功后前往的 URL 地址

addLogoutHandler()方法:添加退出处理器

logoutSuccessHandler()方法:退出成功处理器

退出的表单:

<form id="logoutForm" action="${pageContext.request.contextPath }/my/logout" method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
<script type="text/javascript">
window.onload = function(){
var anchor = document.getElementById("logoutAnchor");
anchor.onclick = function(){
document.getElementById("logoutForm").submit();
return false;
};
};
</script>
<a id="logoutAnchor" href="">退出</a>

(5)基于角色或权限进行访问控制

通过 HttpSecurity 对象设置资源的角色要求

security.authorizeRequests() //对请求进行授权
.antMatchers("/layui/**","/index.jsp") //使用 ANT 风格设置要授权的 URL 地址
.permitAll() //允许上面使用 ANT 风格设置的全部请求
.antMatchers("/red/**")
.hasRole("外门弟子")
.antMatchers("/green/**")
.hasRole("内门弟子")
.antMatchers("/yellow/**")
.hasRole("亲传弟子")
.anyRequest() //其他未设置的全部请求
.authenticated() //需要认证
.and()
.formLogin() //设置未授权请求跳转到登录页面:开启表单登录功
能
.loginPage("/index.jsp") //指定登录页
.permitAll() //为登录页设置所有人都可以访问
.defaultSuccessUrl("/main.html") //设置登录成功后默认前往的 URL 地址
.and()
.logout()
.logoutUrl("/my/logout")
.logoutSuccessUrl("/index.jsp");

通过 AuthenticationManagerBuilder 对象设置用户登录时具备的角色

builder.inMemoryAuthentication()
.withUser("zhangsan").password("123123") //设置账号密码
.roles("ADMIN","外门弟子","亲传弟子") //设置角色
.and()
.withUser("lisi").password("456456")//设置另一个账号密码
.authorities("SAVE","EDIT"); //设置权限

注意:调用顺序

.antMatchers("/red/**") //设置匹配/level1/**的地址
.hasRole("外门弟子") //要求具备“学徒”角色
.antMatchers("/green/**")
.hasRole("内门弟子")
.antMatchers("/yellow/**")
.hasRole("亲传弟子")
.anyRequest() //其实未设置的所有请求
.authenticated() //需要认证才可以访问

后面这两句范围更大,如果.(anyRequest() //其实未设置的所有请求
.authenticated() //需要认证才可以访问)这两句放在前面会把那几句给覆盖掉。

注意:SpringSecurity 会在角色字符串前面加“ROLE_”前缀

之所以要强调这个事情,是因为将来从数据库查询得到的用户信息、角色信息、权 限信息需要我们自己手动组装。手动组装时需要我们自己给角色字符串前面加“ROLE_” 前缀。

(6)定义一个错误页面

代码就不贴了,简单的报个错,就说是没有权限就行了。

前往自定义页面的方式:

@RequestMapping("/to/no/auth/page")
public String toNoAuthPage() {
return "no_auth";
}

HttpSecurity 对象.exceptionHandling().accessDeniedPage("/to/no/auth/page");

(7)启用令牌仓库功能

JdbcTokenRepositoryImpl repository = new JdbcTokenRepositoryImpl();

repository.setDataSource(dataSource);

HttpSecurity 对象.tokenRepository(repository);

注意:需要进入 JdbcTokenRepositoryImpl 类中找到创建 persistent_logins 表的 SQL 语句创建persistent_logins 表。

CREATE TABLE persistent_logins (
username VARCHAR (64) NOT NULL, series VARCHAR (64) PRIMARY KEY, token VARCHAR (64) NOT NULL,
last_used TIMESTAMP NOT NULL
);

(8)查询数据库完成认证

builder.jdbcAuthentication().usersByUsernameQuery("tom");

在usersByUsernameQuery("tom")等方法中最终调用JdbcDaoImpl类的方法查询 数据库。

SpringSecurity 的默认实现已经将 SQL 语句硬编码在了 JdbcDaoImpl 类中。这种 情况下,我们有下面三种选择:

 按照 JdbcDaoImpl 类中 SQL 语句设计表结构。

 修改 JdbcDaoImpl 类的源码。

 不使用 jdbcAuthentication()。

自定义数据查询方式:

builder.userDetailsService(userDetailsService)

其中userDetailsService需要自定义实现UserDetailsService接口的类并自动装配。

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
//1.使用 SQL 语句根据用户名查询用户对象
String sql = "SELECT id,loginacct,userpswd,username,email,createtime FROM t_admin
WHERE loginacct = ?";
//2.获取查询结果
Map<String, Object> resultMap = jdbcTemplate.queryForMap(sql, username);
//3.获取用户名、密码数据
String loginacct = resultMap.get("loginacct").toString();
String userpswd = resultMap.get("userpswd").toString();
//4.创建权限列表
List<GrantedAuthority> list = AuthorityUtils.createAuthorityList("ADMIN","USER");
//5.创建用户对象
User user = new User(loginacct, userpswd, list);
return user;
}
create table t_admin
(
id int not null auto_increment,
loginacct varchar(255) not null, userpswd char(32) not null, username varchar(255) not null, email varchar(255) not null, createtime char(19), primary key (id)
);

使用自定义 UserDetailsService 完成登录

builder.userDetailsService(userDetailService);

“ROLE_”前缀问题

在 自 定 义 的 UserDetailsService 中 , 使 用org.springframework.security.core.authority.AuthorityUtils.createAuthorityList(String...) 工具方法获取创建 SimpleGrantedAuthority 对象添加角色时需要手动在角色名称前 加“ROLE_”前缀。

(9)应用自定义密码加密规则

public String encode(CharSequence rawPassword) {
return CrowdfundingStringUtils.md5(rawPassword.toString());
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
String result = CrowdfundingStringUtils.md5(rawPassword.toString());
return Objects.equals(result, encodedPassword);
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值