pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
加入過濾器
- 選擇一:web.xml
<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>
- 選擇二:AbstractSecurityWebApplicationInitializer
package cn.johnyu;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {
}
配置安全策略
- 使用配置文件,加入security的名稱空間(暫時不可用)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<security:http auto-config="true">
<security:intercept-url pattern="/**" access="ROLE_SPITTER"></security:intercept-url>
</security:http>
</beans>
- 使用配置類(繼承
WebSecurityConfigurerAdapter
)
package cn.johnyu.security;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
*用來修改過濾器鏈
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**");
web.ignoring().antMatchers("/images/**");
web.ignoring().antMatchers("/js/**");
}
/**
*配置UserDetailService,完成"鑒權"
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.passwordEncoder(new StandardPasswordEncoder("53cr3t"));
}
/**
* 修改攔截器,保護請求
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/work")
.hasAuthority("ROLE_READER")
.antMatchers("/other")
.hasAnyRole("GUEST","READER")
.anyRequest().permitAll()
.and()
.formLogin()
.and()
.exceptionHandling()
.accessDeniedPage("/forbidden")
.and()
.logout()
.logoutUrl("/logout");
}
}
關于登錄頁
- 如果沒有Override configure(HttpSecurity),則會提供一個默認的登錄頁(否則沒有)
- 如果加入
.formLogin()
,則會找回該登錄頁 - 該頁核心內(nèi)容如下:
<form method="post" action="/WebApp_war_exploded/login">
<input type="text" name="username" >
<input type="password" name="password">
<input name="_csrf" type="hidden" value="0386c20f-b2fe-426a-9e0f-d58c0fe254d1"/>
<button type="submit">Sign in</button>
</form>
- 你可以加入自定義的登錄頁,方法如下:
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=true");
此時,如果有以下控制器
@RequestMapping("/login")
public String login(){
return "login";
}
則會將頁面轉(zhuǎn)向到自定義的頁面上。
關于退出
.and()
.logout()
.logoutUrl("/logout");
這將指定“/logout”為退出鏈接,退出后將回到“/login?logout”
關于role不符合
- 默認將返回403碼
- 指定403頁面的方法
.and()
.exceptionHandling()
.accessDeniedPage("/forbidden")
關于鑒權
- 可以使用
void configure(AuthenticationManagerBuilder)
進行 - 可以使用三種方法進行:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication();
auth.inMemoryAuthentication();
auth.ldapAuthentication();
}
- 無論哪種方式,都將采用hash方式處理密碼
使用JDBC進行鑒權
- 直接使用(無定制方式)
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.passwordEncoder(new StandardPasswordEncoder("53cr3t"));//這個是必選項
}
- Security默認使用以下表的結構:schema.sql
-- ----------------------------
-- 用戶表: username做為唯一字段
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(8) NOT NULL AUTO_INCREMENT,
`username` varchar(20) DEFAULT NULL,
`password` varchar(100) DEFAULT NULL,
`enabled` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`id`)
);
-- ----------------------------
-- 角色表:username外鍵關聯(lián)users
-- 注意:它與用戶表,存在著間接的 Many To Many 的關系,用戶的角色可以來自:直接角色和所屬組的角色
-- ----------------------------
DROP TABLE IF EXISTS `authorities`;
CREATE TABLE `authorities` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) DEFAULT NULL,
`authority` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
);
-- ----------------------------
-- 用戶組:
-- 注意:它與users存在 Many to Many的關系,但與authorities,是Many To One 的關系
-- ----------------------------
DROP TABLE IF EXISTS `groups`;
CREATE TABLE `groups` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`groupname` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
);
-- ----------------------------
-- users 與 authorites 的一對一的關聯(lián)表,group_Id外鍵關聯(lián)groups表
-- ----------------------------
DROP TABLE IF EXISTS `group_authorities`;
CREATE TABLE `group_authorities` (
`group_Id` int(11) NOT NULL AUTO_INCREMENT,
`authority` varchar(50) DEFAULT NULL,
PRIMARY KEY (`group_Id`)
);
-- ----------------------------
-- 用戶 與 用戶組 之間的多到多關聯(lián)關系(username外鍵關聯(lián)users表,group_Id外鍵關聯(lián)groups表)
-- ----------------------------
DROP TABLE IF EXISTS `group_members`;
CREATE TABLE `group_members` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) DEFAULT NULL,
`group_Id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
);
初如化的數(shù)據(jù):data.sql
-- 密碼為123(salt=53cr3t new StandardPasswordEncoder("53cr3t").encode("123"))
insert into users (username,password,enabled) values('john','97c1e5a28f4d983316d375550e1e28ee50b6d27b5c5a71d6daa4c694227a8d8d94e8e6917ca4ad13',1);
insert into users (username,password,enabled) values('tom','123',0);
insert into users (username,password,enabled) values('alice','221a34d7021d75c196b80cbeb0ce53818871ab2a27be7113afc1ee1d08ab157c19cdef4490a06080',1);
-- 必須確保User具有Role
insert into authorities (username,authority) values('john','ROLE_READER');
insert into authorities (username,authority) values('tom','ROLE_READER');
insert into authorities (username,authority) values('alice','ROLE_GUEST');
附:三條核心查詢語句:
public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService, MessageSourceAware {
//用于用戶身份認證
public static final String DEF_USERS_BY_USERNAME_QUERY = "select username,password,enabled from users where username = ?";
//查詢用戶的角色
public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY = "select username,authority from authorities where username = ?";
//查詢用戶所屬的組的角色
public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY = "select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id";