實習週記(三):學了一半的spring security,看了一遍的swagger,不太熟悉的定時與郵件
SpringSecurity
提供了一套Web應用安全性的完整解決方案
使用者認證
驗證某個使用者是否為系統中的合法主體,通俗意義上為系統驗證使用者是否能夠登入
使用者授權
驗證某個使用者是否有許可權執行某個操作,在一個系統中,不同使用者所就有的許可權是不同的
特點
- 與spring結合性好
- 全面的許可權控制
- 專門為web開發而設計
- 重量級(需要引入較多的依賴)
Demo
- 匯入spring security依賴
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>2.5.3</version> </dependency>
- 編寫controller
@Controller
public class logincontroller {
@GetMapping("/login")
public String login() {
return "login";
}
正常會跳轉至login頁面,但是添加了spring security後,會跳轉至驗證介面,預設使用者名稱為User,密碼為控制檯隨機生成的密碼
原理
實質上是一個過濾器鏈,重點有三個過濾器
FilterSecurityInterceptor
一個方法級的許可權過濾器,位於過濾鏈的底端,控制方法是否能被訪問
ExceptionTranslationFilter
異常過濾器,用於處理在認證授權過程中丟擲的異常
UsernamePasswordAuthenticationFilter
對/login的post請求進行攔截,校驗表單中的使用者名稱和密碼
載入過程
呼叫doFilter中的初始化方法,獲取FilterChainProxy,並通過FilterChainProxy遍歷載入器,放在集合中返回
介面
UserDetailsService
沒有配置時,賬號密碼是由Spring security自定義生成的,為了滿足業務需求,我們需要自定義邏輯控制認證邏輯
PasswordEncoder
資料加密介面,一般用於密碼加密
Web許可權方案
認證
先查詢配置檔案-->查詢配置類-->查詢實現了UserDetailsService介面的實現類
-
通過配置檔案設定
spring.security.user.name=??? spring.security.user.password=???
-
通過配置類實現
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{ @Override protected void configure(AuthenticationManageBuilder auth) throws Exception{ //加密 BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); String password = passwordEncoder.encode("123"); auth.inMemoryAuthentication().withUser("chenchenchen").password(password).roles("admin"); } //如果不建立此物件,會報錯,找不到對應的物件 @Bean PasswordEncoder password(){ return new BCryptPasswordEncoder(); } }
-
自定義實現類
- 建立配置類,設定使用哪個userDetailsService實現類
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter{ @Autowired private UserDetailService userDetailService; @Override protected void configure(AuthenticationManageBuilder auth) throws Exception{ //加密 auth.userDetailsService(userDetailsService).passwordEncoder(password()); } //如果不建立此物件,會報錯,找不到對應的物件 @Bean PasswordEncoder password(){ return new BCryptPasswordEncoder(); } }
- 編寫實現類,返回User物件
@Service public class MyUserDetailsService implements UserDetailsService{ @Override public UserDetails loadUserByUsername(String s) throw UsernameNotFoundException{ List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role"); return new User("chenchenchen",new BCryptPasswordEncoder().encode("123456"),auths)//auths為角色 } }
許可權
- 建立配置類
- 在配置類中設定攔截內容/在controller上使用註解設定許可權
自動登入
實現
-
建立使用者表
CREATE TABLE `persistent_logins` ( `username` varchar(64) NOT NULL, `series` varchar(64) NOT NULL, `token` varchar(64) NOT NULL, `last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`series`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
配置類,注入資料來源
@Configuration public class BrowserSecurityConfig { @Autowired private DataSource dataSource; @Bean public PersistentTokenRepository persistentTokenRepository(){ JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); // 賦值資料來源 jdbcTokenRepository.setDataSource(dataSource); // 自動建立表,第一次執行會建立,如果表存在則應當刪除 jdbcTokenRepository.setCreateTableOnStartup(true); return jdbcTokenRepository; } }
Swagger
- 世界上最流行的Api框架
- 前後端交流的工具
- 即時生成Api文件
- 可線上測試介面
Spring Boot整合Swagger
-
匯入相應依賴包
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency>
-
編寫配置類
@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket docket(){ return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .enable(true) //是否啟用swagger .groupName("陳晨橙") //分組,可以設定多個docket .select() .apis(RequestHandlerSelectors.basePackage("com.shaem.swagger.controller")) //定義掃描的包 //攔截除了shaem以外的介面 .paths(PathSelectors.ant("/shaem/**")) .build(); } public ApiInfo apiInfo(){ return new ApiInfoBuilder() .title("陳晨橙") .description("test") .version("1.0") .termsOfServiceUrl("Url") .contact(new Contact("name","url","email")) .build(); } }
-
重啟專案,訪問測試 http://localhost:8080/swagger-ui.html
配置API分組
- 預設為default,通過groupName()可以配置分組
- 可以通過配置多個Docket達到分組效果
@Bean
public Docket docket1(){
return new Docket(DocumentationType.SWAGGER_2).groupName("group1");
}
@Bean
public Docket docket2(){
return new Docket(DocumentationType.SWAGGER_2).groupName("group2");
}
@Bean
public Docket docket3(){
return new Docket(DocumentationType.SWAGGER_2).groupName("group3");
}
實體類配置
- 使用
ApiModel
標識實體類 - 使用
ApiModelProperty
標識屬性 - 只要請求介面返回值存在實體類,即會出現在文件中
@ApiModel("使用者實體")
public class User {
@ApiModelProperty("使用者名稱")
public String username;
@ApiModelProperty("密碼")
public String password;
}
@RequestMapping("/getUser")
public User getUser(){
return new User();
}
常用註解
下面列一些經常用到的,未列舉出來的可以另行查閱說明:
Swagger註解 | 簡單說明 |
---|---|
@Api(tags = "xxx模組說明") | 作用在模組類上 |
@ApiOperation("xxx介面說明") | 作用在介面方法上 |
@ApiModel("xxxPOJO說明") | 作用在模型類上:如VO、BO |
@ApiModelProperty(value = "xxx屬性說明",hidden = true) | 作用在類方法和屬性上,hidden設定為true可以隱藏該屬性 |
@ApiParam("xxx引數說明") | 作用在引數、方法和欄位上,類似@ApiModelProperty |
定時、非同步與郵件
定時
spring提供兩個介面讓我們能實現定時執行任務的功能
- Task Executor介面
- Task Scheduler介面
在微服務中,我們通常使用註解來實現
- @EnableScheduling(新增在啟動類上)
- @Scheduled(cron=“表示式”) //新增在想要執行的方法上
cron表示式
一、結構
corn從左到右(用空格隔開):秒 分 小時 月份中的日期 月份 星期中的日期 年份
二、各欄位的含義
欄位 | 允許值 | 允許的特殊字元 |
---|---|---|
秒(Seconds) | 0~59的整數 | , - * / 四個字元 |
分(Minutes) | 0~59的整數 | , - * / 四個字元 |
小時(Hours) | 0~23的整數 | , - * / 四個字元 |
日期(DayofMonth) | 1~31的整數(但是你需要考慮你月的天數) | ,- * ? / L W C 八個字元 |
月份(Month) | 1~12的整數或者 JAN-DEC | , - * / 四個字元 |
星期(DayofWeek) | 1~7的整數或者 SUN-SAT (1=SUN) | , - * ? / L C # 八個字元 |
年(可選,留空)(Year) | 1970~2099 | , - * / 四個字元 |
注意事項:
每一個域都使用數字,但還可以出現如下特殊字元,它們的含義是:
(1)✳:表示匹配該域的任意值。假如在Minutes域使用*, 即表示每分鐘都會觸發事件。
(2)?:只能用在DayofMonth和DayofWeek兩個域。它也匹配域的任意值,但實際不會。因為DayofMonth和DayofWeek會相互影響。例如想在每月的20日觸發排程,不管20日到底是星期幾,則只能使用如下寫法: 13 13 15 20 * ?, 其中最後一位只能用?,而不能使用,如果使用表示不管星期幾都會觸發,實際上並不是這樣。
(3)-:表示範圍。例如在Minutes域使用5-20,表示從5分到20分鐘每分鐘觸發一次
(4)/:表示起始時間開始觸發,然後每隔固定時間觸發一次。例如在Minutes域使用5/20,則意味著5分鐘觸發一次,而25,45等分別觸發一次.
(5),:表示列出列舉值。例如:在Minutes域使用5,20,則意味著在5和20分每分鐘觸發一次。
(6)L:表示最後,只能出現在DayofWeek和DayofMonth域。如果在DayofWeek域使用5L,意味著在最後的一個星期四觸發。
(7)W:表示有效工作日(週一到週五),只能出現在DayofMonth域,系統將在離指定日期的最近的有效工作日觸發事件。例如:在 DayofMonth使用5W,如果5日是星期六,則將在最近的工作日:星期五,即4日觸發。如果5日是星期天,則在6日(週一)觸發;如果5日在星期一到星期五中的一天,則就在5日觸發。另外一點,W的最近尋找不會跨過月份 。
(8)LW:這兩個字元可以連用,表示在某個月最後一個工作日,即最後一個星期五。
(9)#:用於確定每個月第幾個星期幾,只能出現在DayofMonth域。例如在4#2,表示某月的第二個星期三。
非同步
傳送郵件一般不能立刻傳送,讓使用者等待會降低體驗,這個時候就可以用非同步來模擬同步效果,讓後臺執行傳送郵件的功能,其中涉及到多執行緒
- 在啟動類上新增
@EnableAsync
- 在方法上使用
@Async
- 測試
郵件
在spring boot中使用郵件服務
-
引入依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>
-
配置檔案
[email protected] #授權碼可以在郵箱中的賬號中找到 spring.mail.password=qq授權碼 spring.mail.host=smtp.qq.com # qq需要配置ssl spring.mail.properties.mail.smtp.ssl.enable=true
-
測試
SimpleMailMessage message = new SimpleMailMessage(); message.setSubject("通知-今晚早點下班"); message.setText("今晚7:00下班"); message.setTo("[email protected]"); message.setFrom("[email protected]"); mailSender.send(message);
MimeMessage mimeMessage = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true); helper.setSubject("通知"); helper.setText("<b style='color:red'>今天 6:30下班</b>",true); //傳送附件 helper.addAttachment("1.jpg",new File("")); helper.addAttachment("2.jpg",new File("")); helper.setTo("[email protected]"); helper.setFrom("[email protected]"); mailSender.send(mimeMessage);