概述
在前不久用了spring boot、mybatis、spring security搭建了一个工程,中间经历了各种坑,最后得到一条经验:spring的文档很详细,入门最好以官方文档为准。
这里讲的是以mav作为依赖管理工具
pom
搭建spring boot应用快捷的方式是在pom.xml中引入spring-boot-starter-parent 作为parent,如下:
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>com.tw</groupId>
- <artifactId>twboot</artifactId>
- <packaging>war</packaging>
- <version>0.0.1-SNAPSHOT</version>
- <name>twboot Maven Webapp</name>
- <url>http://maven.apache.org</url>
- <properties>
- <mybatis-spring-boot>1.2.0</mybatis-spring-boot>
- </properties>
- <!-- Spring Boot 启动父依赖 -->
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>1.5.9.RELEASE</version>
- </parent>
- <dependencies>
- <!-- 模板引擎jar包, 已经包括了spring-boot-starter-web -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-thymeleaf</artifactId>
- </dependency>
- <!-- Spring Boot Test 依赖 -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
- <!-- security -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-security</artifactId>
- </dependency>
- <!-- 用于thymeleaf中使用security的标签 -->
- <dependency>
- <groupId>org.thymeleaf.extras</groupId>
- <artifactId>thymeleaf-extras-springsecurity4</artifactId>
- </dependency>
- <!-- Spring Boot Mybatis 依赖 -->
- <dependency>
- <groupId>org.mybatis.spring.boot</groupId>
- <artifactId>mybatis-spring-boot-starter</artifactId>
- <version>${mybatis-spring-boot}</version>
- </dependency>
- <!-- Junit -->
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- </dependency>
- <!-- 上传组件包 -->
- <dependency>
- <groupId>commons-fileupload</groupId>
- <artifactId>commons-fileupload</artifactId>
- <version>1.3.1</version>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.4</version>
- </dependency>
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>servlet-api</artifactId>
- <version>2.5</version>
- </dependency>
- </dependencies>
- <build>
- <finalName>twboot</finalName>
- </build>
- </project>
application.yml
spring boot 中默认会获取classpath下的application.yml或者application.propertis文件作为配置文件
我用的前者
- server:
- port: 80
- spring:
- datasource:
- username: xyz
- password: xyz
- driver-class-name: oracle.jdbc.driver.OracleDriver
- url: jdbc:oracle:thin:@127.0.0.1:1521:XE
- dbcp2:
- max-idle: 10
- validation-query: select 1 from dual
- thymeleaf:
- cache: false
- prefix: classpath:/templates/
- suffix: .html
- mybatis:
- type-aliases-package: com.tw.entity
- mapper-locations:
- - classpath:mapping/*.xml
上面的server.port 表示spring boot内置tomcat启动的端口是80,view层显示用的thymeleaf模板,模板前后缀也在配置文件中定义,这个和spring mvc那种配置类似;
另工程中只用用一种view表示方式,用了thymeleaf就不要用jsp了,网上说的两种模板解析链是有先后处理顺序的,也就是说是有优先级;
Springboot启动类
在我们的package最顶层新建一个继承SpringBootServletInitializer的类(内容如下),这样一个spring boot项目就搭建好了
- package com.tw;
- import org.mybatis.spring.annotation.MapperScan;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.boot.builder.SpringApplicationBuilder;
- import org.springframework.boot.web.support.SpringBootServletInitializer;
- @SpringBootApplication
- @MapperScan("com.tw.dao")
- public class StartApp extends SpringBootServletInitializer {
- public static void main(String[] args) {
- SpringApplication.run(StartApp.class);
- }
- //添加Tomcat支持
- @Override
- protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
- return application.sources(StartApp.class);
- }
- }
@SpringBootApplication 表面这是一个springboot应用,
@MapperScan("com.tw.dao") 这个表示
mybatis自动扫描dao接口的包名,
这个包下的接口会自动和spring boot配置项mapper-locations中的mybatis sql配置文件映射
@RestController
@Controller
我这里写一个简单的controller
- package com.tw.controller;
- import java.io.IOException;
- import java.io.UnsupportedEncodingException;
- import java.util.Map;
- import javax.servlet.ServletResponse;
- import org.springframework.security.access.prepost.PreAuthorize;
- import org.springframework.stereotype.Controller;
- import org.springframework.ui.Model;
- import org.springframework.web.bind.annotation.RequestMapping;
- @Controller
- public class CommonController {
- @PreAuthorize("hasRole('RRR')")
- @RequestMapping(value={"/","/home"})
- public String index() {
- return "home/home";
- }
- @RequestMapping("/login")
- public String login(Model model) {
- System.out.println("to login----");
- return "login/login";
- }
- @RequestMapping("/thymeleaf")
- public String test(Map<String,Object> map,ServletResponse response) throws UnsupportedEncodingException, IOException{
- map.put("name", "test");
- System.out.println("thymeleaf----");
- return "thymeleaf/hello2";
- }
- /**
- * 功能描述:角色管理
- * CHENY037 2017年11月29日
- * @return
- */
- @RequestMapping("/roleManage")
- @PreAuthorize("hasRole('ROLE_MANAGE')")
- public String roleManage(){
- return "home/role";
- }
- }
spring security
自定义登录界面和自定义用户名密码校验
配置类:
- package com.tw.config.Security;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Bean;
- 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.configuration.EnableWebSecurity;
- import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
- import org.springframework.security.core.session.SessionRegistry;
- import org.springframework.security.core.session.SessionRegistryImpl;
- import org.springframework.security.crypto.password.PasswordEncoder;
- /**
- * 配置类:
- * 配置security的登录页面和传递的参数,公共路径权限属性等
- *
- * @author CHENY037
- *
- */
- @Configuration
- @EnableWebSecurity
- public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
- @Autowired
- private UrlUserService urlUserService;
- @Autowired
- SessionRegistry sessionRegistry;
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http.csrf().disable()
- .authorizeRequests()
- .antMatchers("/login").permitAll()
- // .antMatchers("/logout").permitAll()
- .antMatchers("/img/**").permitAll()
- .antMatchers("/js/**").permitAll()
- .antMatchers("/css/**").permitAll()
- .antMatchers("/bootstrap/**").permitAll()
- .antMatchers("/fonts/**").permitAll()
- .antMatchers("/favicon.ico").permitAll()
- .anyRequest().authenticated()
- //登录相关
- .and().formLogin().loginPage("/login").usernameParameter("username").passwordParameter("password").defaultSuccessUrl("/home")
- .and().sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry)
- .and().and()
- .logout()
- .invalidateHttpSession(true)
- .clearAuthentication(true)
- .and()
- .httpBasic();
- }
- @Autowired
- public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
- //这里是新增一个默认用户
- auth.inMemoryAuthentication().withUser("huahua").password("hello").roles("ADMIN");
- }
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- auth.userDetailsService(urlUserService).passwordEncoder(new PasswordEncoder() {
- @Override
- public String encode(CharSequence rawPassword) {
- return (String)rawPassword;//MD5Util.encode((String) rawPassword);
- }
- @Override
- public boolean matches(CharSequence rawPassword, String encodedPassword) {
- System.out.println(encodedPassword + "---" + (String)rawPassword);
- return encodedPassword.equals((String) rawPassword);
- }
- });
- }
- @Bean
- public SessionRegistry getSessionRegistry(){
- SessionRegistry sessionRegistry = new SessionRegistryImpl();
- return sessionRegistry;
- }
- }
- package com.tw.config.Security;
- import java.util.ArrayList;
- import java.util.List;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.security.core.GrantedAuthority;
- import org.springframework.security.core.authority.SimpleGrantedAuthority;
- import org.springframework.security.core.userdetails.User;
- import org.springframework.security.core.userdetails.UserDetails;
- import org.springframework.security.core.userdetails.UserDetailsService;
- import org.springframework.security.core.userdetails.UsernameNotFoundException;
- import org.springframework.stereotype.Service;
- import com.tw.dao.RoleDao;
- import com.tw.dao.UserDao;
- import com.tw.entity.role.TwRole;
- import com.tw.entity.role.TwUser;
- /**
- * 自定义用户名密码校验实现
- *
- * @author CHANY037 2017-11
- *
- */
- @Service
- public class UrlUserService implements UserDetailsService {
- @Autowired
- UserDao userDao;
- @Autowired
- RoleDao roleDao;
- /**
- * employeeId 用户工号,在数据库中保证存储唯一
- */
- @Override
- public UserDetails loadUserByUsername(String employeeId) { //重写loadUserByUsername 方法获得 userdetails 类型用户
- TwUser user = userDao.findByEmployeeId(employeeId);
- if(user == null){
- throw new UsernameNotFoundException(employeeId + " do not exist!");
- } else {
- System.out.println(user.getPassword() + " --pw-- ");
- List<TwRole> roles = roleDao.findRoleByEmployeeId(employeeId);
- List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
- //写入用户的角色 *** 切记 由于框架原因 角色名称要以 ROLE_ 开头 **** 血泪史 ****
- //源码:org.springframework.security.access.expression.SecurityExpressionRoot hasAnyRole()
- for (TwRole role : roles) {
- if (role != null && role.getRoleName() != null) {
- SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getRoleCode());
- grantedAuthorities.add(grantedAuthority);
- }
- }
- org.springframework.security.core.userdetails.User uu = new User(employeeId, user.getPassword(), grantedAuthorities);
- return uu;
- }
- }
- }
登录界面html 即 上面
- CommonController 里定义的login方法返回的视图
- <!doctype html>
- <html lang="en" class="no-js"
- xmlns="http://www.w3.org/1999/xhtml"
- xmlns:th="http://www.thymeleaf.org"
- xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
- <head>
- <meta charset="UTF-8"/>
- <title>Login</title>
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
- <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
- <!-- thymeleaf 默认从resources 下的 static目录获取 -->
- <link rel="stylesheet" th:href="@{bootstrap/css/bootstrap.min.css}"/>
- <script th:src="@{js/jquery.min.js}"></script>
- <script th:src="@{bootstrap/js/bootstrap.min.js}"></script>
- <style>
- .form-control{
- width: 200px;
- }
- </style>
- </head>
- <body style="background: url(img/login.jpg);width: 98%;">
- <div style="margin-top: 150px;margin-left: 800px;margin-bottom: 160px;" >
- <form class="form-horizontal" method="post" th:action="@{/login}">
- <div class="form-group">
- <label for="inputEmail3" class="col-sm-2 control-label">用户名</label>
- <div class="col-sm-10"><input autocomplete="off" type="text" class="form-control" name="username" /></div>
- </div>
- <div class="form-group">
- <label for="inputPassword3" class="col-sm-2 control-label">密码</label>
- <div class="col-sm-10">
- <input type="text" class="form-control" name="password" οnfοcus="this.type='password'"/>
- </div>
- </div>
- <div class="form-group">
- <div class="col-sm-offset-2 col-sm-10">
- <div class="checkbox">
- <label><input type="checkbox" />Remember me</label><!-- 功能暂未实现 -->
- </div>
- </div>
- </div>
- <div class="form-group">
- <div class="col-sm-offset-2 col-sm-10">
- <button type="submit" class="btn btn-default">登录</button>
- </div>
- </div>
- </form>
- </div>
- <!-- 引入 页脚 -->
- <div th:replace="common/footer :: footer"></div>
- </body>
- </html>